Make a clean environment
rm(list=ls())
Load packages
packages.list <- c("ggplot2","treeio","ggtree","ggnewscale","ape","dplyr","tidyverse","tidyr","phytools","RColorBrewer","lubridate","readxl","ggforce","ggstance","ggridges","cowplot","hexbin","scales","haven","network","ggnetwork","intergraph","igraph","ggraph","graphlayouts","scatterpie","maps","mapdata","maptools","rgdal","rgeos","broom","ggrepel","ggridges","magick","ggbeeswarm")
#"plyr","Cairo","ggmap","emojifont","rPinecone","pairsnp","CoordinateCleaner","gridExtra","dendextend","ggdendro",
#BiocManager::install("ggtree")
#BiocManager::install("treeio")
for(pkg in packages.list){
eval(bquote(library(.(pkg)))) }
Confirm current environmental setup
R.Version()
$platform
[1] "x86_64-apple-darwin17.0"
$arch
[1] "x86_64"
$os
[1] "darwin17.0"
$system
[1] "x86_64, darwin17.0"
$status
[1] ""
$major
[1] "4"
$minor
[1] "1.2"
$year
[1] "2021"
$month
[1] "11"
$day
[1] "01"
$`svn rev`
[1] "81115"
$language
[1] "R"
$version.string
[1] "R version 4.1.2 (2021-11-01)"
$nickname
[1] "Bird Hippie"
print(sessionInfo())
R version 4.1.2 (2021-11-01)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Monterey 12.6.1
Matrix products: default
LAPACK: /Library/Frameworks/R.framework/Versions/4.1/Resources/lib/libRlapack.dylib
locale:
[1] en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] magick_2.7.3 ggrepel_0.9.1 broom_1.0.0 rgeos_0.5-9 rgdal_1.5-30
[6] maptools_1.1-4 mapdata_2.3.0 scatterpie_0.1.7 graphlayouts_0.7.2 ggraph_2.0.5
[11] igraph_1.3.5 intergraph_2.0-2 ggnetwork_0.5.10 network_1.17.1 haven_2.4.3
[16] scales_1.2.1 hexbin_1.28.2 ggridges_0.5.3 ggstance_0.3.5 ggforce_0.3.3
[21] readxl_1.3.1 lubridate_1.8.0 RColorBrewer_1.1-3 ggnewscale_0.4.5 treeio_1.18.1
[26] phytools_0.7-90 maps_3.4.0 phangorn_2.8.0 ggtree_3.2.1 ape_5.6-2
[31] vcfR_1.13.0 cowplot_1.1.1 ggbeeswarm_0.6.0 forcats_0.5.1 stringr_1.4.1
[36] purrr_0.3.4 readr_2.1.3 tidyr_1.1.4 tibble_3.1.8 ggplot2_3.3.6
[41] tidyverse_1.3.2 dplyr_1.0.7 sp_1.4-6 Matrix_1.3-4
loaded via a namespace (and not attached):
[1] backports_1.4.0 Hmisc_4.7-0 fastmatch_1.1-3 plyr_1.8.6
[5] lazyeval_0.2.2 splines_4.1.2 digest_0.6.30 yulab.utils_0.0.4
[9] htmltools_0.5.3 viridis_0.6.2 fansi_1.0.3 magrittr_2.0.3
[13] checkmate_2.1.0 googlesheets4_1.0.0 cluster_2.1.2 tzdb_0.3.0
[17] sna_2.6 modelr_0.1.8 jpeg_0.1-9 colorspace_2.0-3
[21] rvest_1.0.2 xfun_0.34 crayon_1.5.2 jsonlite_1.8.3
[25] survival_3.2-13 glue_1.6.2 polyclip_1.10-0 gtable_0.3.1
[29] gargle_1.2.0 seqinr_4.2-16 BiocGenerics_0.40.0 DBI_1.1.1
[33] Rcpp_1.0.9 plotrix_3.8-2 viridisLite_0.4.1 htmlTable_2.4.1
[37] tmvnsim_1.0-2 gridGraphics_0.5-1 tidytree_0.3.6 foreign_0.8-81
[41] Formula_1.2-4 stats4_4.1.2 htmlwidgets_1.5.4 httr_1.4.4
[45] geosphere_1.5-14 ellipsis_0.3.2 pkgconfig_2.0.3 farver_2.1.1
[49] nnet_7.3-16 sass_0.4.2 dbplyr_2.1.1 deldir_1.0-6
[53] utf8_1.2.2 ggplotify_0.1.0 tidyselect_1.2.0 labeling_0.4.2
[57] rlang_1.0.6 reshape2_1.4.4 munsell_0.5.0 cellranger_1.1.0
[61] tools_4.1.2 cachem_1.0.6 cli_3.4.1 generics_0.1.1
[65] statnet.common_4.5.0 ggmap_3.0.0 ade4_1.7-19 evaluate_0.17
[69] fastmap_1.1.0 yaml_2.3.6 knitr_1.40 fs_1.5.2
[73] tidygraph_1.2.0 RgoogleMaps_1.4.5.3 nlme_3.1-153 RcppRoll_0.3.0
[77] aplot_0.1.1 xml2_1.3.3 compiler_4.1.2 rstudioapi_0.13
[81] beeswarm_0.4.0 png_0.1-7 clusterGeneration_1.3.7 reprex_2.0.1
[85] tweenr_1.0.2 bslib_0.4.0 stringi_1.7.8 memuse_4.2-1
[89] lattice_0.20-45 vegan_2.6-2 permute_0.9-7 vctrs_0.5.0
[93] pillar_1.8.1 lifecycle_1.0.3 combinat_0.0-8 jquerylib_0.1.4
[97] bitops_1.0-7 data.table_1.14.2 patchwork_1.1.1 R6_2.5.1
[101] latticeExtra_0.6-30 gridExtra_2.3 vipor_0.4.5 IRanges_2.28.0
[105] codetools_0.2-18 MASS_7.3-54 assertthat_0.2.1 rjson_0.2.20
[109] withr_2.5.0 pinfsc50_1.2.0 mnormt_2.0.2 S4Vectors_0.32.4
[113] expm_0.999-6 mgcv_1.8-38 parallel_4.1.2 hms_1.1.2
[117] fastbaps_1.0.6 quadprog_1.5-8 grid_4.1.2 pairsnp_0.1.0
[121] rpart_4.1-15 ggfun_0.0.4 coda_0.19-4 rmarkdown_2.17
[125] googledrive_2.0.0 Cairo_1.5-12.2 scatterplot3d_0.3-41 numDeriv_2016.8-1.1
[129] base64enc_0.1-3 interp_1.1-3
Make some shortcuts for plotting
y.theme.strip <- theme(axis.title.y = element_blank(), axis.text.y = element_blank(), axis.ticks.y= element_blank())
y.theme.strip.partial <- theme(axis.text.y = element_blank(), axis.ticks.y= element_blank())
x.theme.strip <- theme(axis.title.x = element_blank(), axis.text.x = element_blank(), axis.ticks.x= element_blank())
x.theme.strip.partial <- theme(axis.text.x = element_blank(), axis.ticks.x= element_blank())
x.theme.strip.labs <- theme(axis.text.x = element_blank(),axis.title.x = element_blank())
x.theme.axis.rotate <- theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
legend.strip <- theme(legend.position = "none")
theme.text.size <- theme(text = element_text(size = 10))
'%notin%' <- Negate('%in%')
max.font.size <- 7
basic.font.size <- 6
min.font.size <- 5.25
theme.text.size <- theme(text = element_text(size = basic.font.size))
theme.text.size.within <- (5/14)*min.font.size
panel.lab.size <- 10
Specify raw data - global dataset
#Data_input_directory <- "/Users/mb29/Papers/Treponema_UK-PHE-gen-epi_2021/Data/"
#Data_input_directory <- "/Users/mb29/Papers/Treponema_UK-PHE-gen-epi_2021/Rnotebook/Rnotebook_09-2022/data/"
Data_input_directory <- paste0(getwd(), "/inputdata/")
################################
#### Tree data
# ML tree (refined dataset)
TPA.MLtree.file <- paste0(Data_input_directory,"TPA-uber.remasked.2020-11-10.goodcov25.gubbins.SNPs.aln.renamed.fix-zero-dist.treefile")
# Pyjar tree (refined dataset)
TPA.pyjar.file <- paste0(Data_input_directory,"TPA-uber.remasked.2020-11-10.goodcov25.gubbins.SNPs.aln.renamed.pyjar.tre")
# Full size BEAST2 analysis - previously generated as part of Beale, 2021.
full.beast2.tree.file <- paste0(Data_input_directory,"TPA-uber_beast2_strict-skyline-500M_10pop_consensus.tree")
################################
#### Meta data
# Supplement from TPA-Uber paper - Beale, 2021
TPA.meta2.file <- paste0(Data_input_directory,"Sup_Data1_Global_Sample-Metadata__09-2022.xlsx")
# England specific metadata collated by PHE/UKHSA
PHE.metadata.linked.file <- paste0(Data_input_directory,"Sup_Data2_TPA.UK-only.PHE.metadata.2022-02-02.xlsx")
# England specific mapping shapefile data with Public Health Boundaries
# Imported datafile from https://geoportal.statistics.gov.uk/datasets/public-health-england-centres-december-2016-full-clipped-boundaries-in-england/explore?location=52.950000%2C-2.000000%2C6.88
UK.publichealth.shapefile.data <- paste0(Data_input_directory,"Public_Health_England_Centres_(December_2016)_Boundaries")
################################
#### Externally plotted figures (e.g. GrapeTree) for inclusion in multipanel figures
# Externally plotted grapetree minimum spanning tree for whole of England - code to extract subtree that was used to make this is included later in this Rnotebook
TPA.UK.Grapetree.sublineages.file <- paste0(Data_input_directory,"TPA-UK-2022-02-03.sublineage-MSTree.Inkscaped.svg")
# Externally plotted grapetree minimum spanning tree for whole of England - 3-variable plots
TPA.UK.Grapetree.3way.file <- paste0(Data_input_directory,"TPA-UK-2022-02-16.-MSTree_3-way-figure.Inscaped-3.svg")
# Externally plotted grapetree minimum spanning tree for whole of England - HIV status
TPA.UK.Grapetree.HIV.file <- paste0(Data_input_directory,"TPA-UK-2022-02-03.HIVstatus-MSTree_inkscaped.svg")
# Externally plotted grapetree minimum spanning tree for North East England networks
TPA.NorthEastEngland.Grapetree.file <- paste0(Data_input_directory,"TPA-UK-NorthEast-2022-02-26.GenderOrientation-MSTree.inkscaped.+node-counts+GBMSM.svg")
Specify directory to output plots
Figure_output_directory <- paste0(getwd(), "/Figures/")
#"/Users/mb29/Papers/Treponema_UK-PHE-gen-epi_2021/Figures/Figure_Drafting/Working_Figures_08-2022/"
Read in trees
TPA.MLtree <- midpoint.root(read.tree(TPA.MLtree.file))
TPA.pyjar.tree <- midpoint.root(read.tree(TPA.pyjar.file))
Read in final output metadata from Global Uber study (Beale 2021)
TPA.meta2.1 <- readxl::read_excel(TPA.meta2.file,sheet="Supplementary_Data1_Sample-Meta")
Create a colour scheme for Lineages, Countries and Continents (consistent with Beale, 2021)
# Colouring for country
continental.country.cols.brew2 <- unique(TPA.meta2.1[,c("Geo_Country","Continent")])
continental.country.cols.brew2 <- continental.country.cols.brew2[order(continental.country.cols.brew2$Continent,continental.country.cols.brew2$Geo_Country),]
continental.country.cols.brew2$country.col <- c("#ec7014","#fec44f","#de2d26","#fb6a4a","#bdbdbd","#737373",brewer.pal(n=8,"Purples")[4:8],brewer.pal(n=8,"Blues")[3:8],brewer.pal(n=5,"Greens")[3:5],"#c51b8a","#8c510a")
# Colouring for Continent
continental.cols.brew2 <- data.frame(Continent=sort(unique(TPA.meta2.1$Continent)),stringsAsFactors=F)
continental.cols.brew2$continent.col <- c("#fec44f","#de2d26","#bdbdbd","#2171b5","#74c476","#c51b8a","#ec7014")
# Colouring for TPA Lineage
TPA_Lineage.cols <- data.frame(Lineage=sort(unique(TPA.meta2.1$TPA_Lineage)),stringsAsFactors=F)
TPA_Lineage.cols$Lineage.col <- c("royalblue2", "indianred1")
#c("#436eee", "#666666","#ff6a6a")
TPA_Lineage.cols$Lineage <- factor(TPA_Lineage.cols$Lineage, levels=c("Nichols","SS14","outlier"))
# Lineage Hexcodes
# royalblue2 #436eee
# indianred1 #ff6a6a
Define colours for sublineages
# Define sublineage clustering scheme using brew colourscales
sublineages.cols.brew <- data.frame(unique(TPA.meta2.1[,c("TPA_Lineage","TPA.pinecone.sublineage")]), stringsAsFactors = F)
sublineages.cols.brew <- sublineages.cols.brew[order(sublineages.cols.brew$TPA_Lineage,sublineages.cols.brew$TPA.pinecone.sublineage),]
sublineages.cols.brew$sublin.order <- as.numeric(as.character(sublineages.cols.brew$TPA.pinecone.sublineage))
Warning: NAs introduced by coercion
sublineages.cols.brew <- sublineages.cols.brew[order(sublineages.cols.brew$sublin.order),]
# For revised bootstrapped clusters
sublineages.cols.brew$sublineage.cols <- c("#FC9272","#EF3B2C",brewer.pal(n=4,"Greens")[2:4],brewer.pal(n=4,"YlOrBr")[c(2,3)],brewer.pal(n=6,"Blues")[2:6],brewer.pal(n=6,"Purples")[2:6],"grey80","grey80","grey80","grey80")
sublineages.cols.brew <- unique(sublineages.cols.brew[,c("TPA.pinecone.sublineage","sublineage.cols")])
sublineages.cols.brew <- sublineages.cols.brew[order(as.numeric(as.character(sublineages.cols.brew$TPA.pinecone.sublineage))),]
Warning in order(as.numeric(as.character(sublineages.cols.brew$TPA.pinecone.sublineage))) :
NAs introduced by coercion
sublineages.cols.brew$TPA.pinecone.sublineage <- factor(sublineages.cols.brew$TPA.pinecone.sublineage, levels=sublineages.cols.brew$TPA.pinecone.sublineage)
sublineages.cols.brew <- sublineages.cols.brew[!is.na(sublineages.cols.brew$sublineage),]
colnames(sublineages.cols.brew) <- c("sublineage","sublineage.cols")
sublineages.cols.brew <- unique(sublineages.cols.brew)
Restrict analysis to high quality genomes (and tree)
TPA.meta2.1 <- TPA.meta2.1[TPA.meta2.1$finescale.analysis=="Yes",]
Create a “UK” variable, and a “PHE” variable
TPA.meta2.1$is.UK <- ifelse(TPA.meta2.1$Geo_Country=="UK","UK","Other")
TPA.meta2.1$is.PHE <- ifelse(TPA.meta2.1$Geo_Country=="UK" & grepl("PHE",TPA.meta2.1$Sample_Name),"PHE","Other")
# Prepare ML tree
TPA.MLtree.ggtree <- ggtree(TPA.MLtree,layout = "fan",open.angle = 10, right=T)
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing
scale.
# Prepare country dataset
TPA.rawseq.countries.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Country=TPA.meta2.1$Geo_Country, stringsAsFactors = F)
# Prepare continent dataset
TPA.rawseq.continents.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Continent=TPA.meta2.1$Continent, stringsAsFactors = F)
# Prepare UK data strip
TPA.rawseq.UK.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, England=TPA.meta2.1$is.UK, stringsAsFactors = F)
TPA.rawseq.UK.p[TPA.rawseq.UK.p$England=="UK",] <- "England"
# Prepare PHE data strip
TPA.rawseq.PHE.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, PHE=TPA.meta2.1$is.PHE, stringsAsFactors = F)
# Prepare Major lineage dataset
TPA.rawseq.Lineage.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Lineage=TPA.meta2.1$TPA_Lineage, stringsAsFactors = F)
# Prepare sublineage lineage dataset
TPA.rawseq.subLineage.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Sublineage=TPA.meta2.1$TPA.pinecone.sublineage, stringsAsFactors = F)
# Prepare Year dataset (all samples)
TPA.rawseq.all.Years.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Year=TPA.meta2.1$Sample_Year, stringsAsFactors = F)
floor_5years <- function(value){ return(value - value %% 5) }
TPA.meta2.1$Sample_5year.window <- paste0(floor_5years(as.numeric(TPA.meta2.1$Sample_Year)),"-",floor_5years(as.numeric(TPA.meta2.1$Sample_Year))+5)
Warning in floor_5years(as.numeric(TPA.meta2.1$Sample_Year)) :
NAs introduced by coercion
Warning in floor_5years(as.numeric(TPA.meta2.1$Sample_Year)) :
NAs introduced by coercion
# Some samples have uncertain dates (up to 20-30 years uncertainty), but for the purposes of these plotting categories we'll use the centrepoint year
TPA.meta2.1$Sample_5year.window <- sapply(1:nrow(TPA.meta2.1), function(x) ifelse(TPA.meta2.1$Sample_Year[x]=="-",NA, ifelse(is.na(TPA.meta2.1$Sample_5year.window[x]),NA, ifelse(TPA.meta2.1$Sample_Year[x]=="1950-1980","1965-1970",ifelse(TPA.meta2.1$Sample_Year[x]=="1960-1980","1965-1970" ,ifelse(TPA.meta2.1$Sample_Year[x]=="1980-1999","1985-1990",TPA.meta2.1$Sample_5year.window[x]))))))
TPA.meta2.1$Sample_year.1990.cuttoff <- ifelse(TPA.meta2.1$Sample_Year>1990,TPA.meta2.1$Sample_Year,"<1990")
TPA.meta2.1$Sample_year.1999.cuttoff <- ifelse(TPA.meta2.1$Sample_Year>1999,TPA.meta2.1$Sample_Year,"<1999")
TPA.rawseq.year.cuttoff.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Sample.Year=TPA.meta2.1$Sample_year.1999.cuttoff, stringsAsFactors = F)
# Bring in PHE metadata
PHE.metadata.linked <- readxl::read_excel(PHE.metadata.linked.file)
Do some cleanup and factoring of variables
PHE.metadata.linked$age_group <- factor(PHE.metadata.linked$age_group, levels=rev(c("16-24","25-34","35-44","45+","Unknown")))
PHE.metadata.linked$london <- factor(PHE.metadata.linked$london,levels=rev(c("Yes","No","Unknown")))
PHE.metadata.linked$ukborn <- factor(PHE.metadata.linked$ukborn,levels=rev(c("Yes","No","Unknown")))
PHE.metadata.linked$hivpos <- factor(PHE.metadata.linked$hivpos, levels=rev(c("Yes","No","Unknown")))
# need to update terminology of 'MSM' to 'GBMSM'
PHE.metadata.linked[PHE.metadata.linked$gender_orientation=="MSM","gender_orientation"] <- "GBMSM"
PHE.metadata.linked$gender_orientation <- factor(PHE.metadata.linked$gender_orientation, levels=rev(c("MSW","GBMSM","WSM","MUnknown","Unknown")))
PHE.metadata.linked$phe_centre <- factor(PHE.metadata.linked$phe_centre, levels=rev(c("East Midlands", "East of England", "London", "North East", "North West", "South East", "South West", "West Midlands", "Yorkshire and Humber", "UK (not England)", "Not Known")))
PHE.metadata.linked$TPA.pinecone.sublineage <- factor(PHE.metadata.linked$TPA.pinecone.sublineage, levels=sublineages.cols.brew$sublineage)
### Extract information about duplicates
PHE.metadata.duplicates <- PHE.metadata.linked[!is.na(PHE.metadata.linked$dup_flag),]
PHE.metadata.duplicates <- PHE.metadata.duplicates[!is.na(PHE.metadata.duplicates$Sample_Name),]
PHE.patient.matches <- data.frame(
stringsAsFactors = FALSE,
dup_flag = c("1A","1B",
"2A","2B","3A","3B","4A",
"4B","5A","5B"),
dup_Patient = c("Patient 1",
"Patient 1","Patient 2",
"Patient 2","Patient 3","Patient 3",
"Patient 4","Patient 4",
"Patient 5","Patient 5"),
dup_Patient_Sample = c("sample 1",
"sample 2","sample 1",
"sample 2","sample 1","sample 2",
"sample 1","sample 2","sample 1",
"sample 2")
)
PHE.metadata.duplicates <- left_join(PHE.metadata.duplicates, PHE.patient.matches, by="dup_flag")
PHE.metadata.duplicates
Duplicate Samples missing metadata are all ‘new duplicates’ and were excluded due to low mapping coverage (all checked).
Samples labelled ‘ZA’ and ‘XB’ had duplicates in the original dataset, but the reciprocal pairs were excluded due to quality isues.
Available pairs - Patient 3, Patient 4
PHE.metadata.duplicates.paired <- PHE.metadata.duplicates[PHE.metadata.duplicates$dup_Patient %in% c("Patient 3","Patient 4"),]
PHE.metadata.duplicates.paired[order(PHE.metadata.duplicates.paired$dup_Patient, PHE.metadata.duplicates.paired$year,PHE.metadata.duplicates.paired$month),c("Sample_Name","dup_Patient", "month.fix", "year")]
These will be revisited later in the analysis.
Patient 4 HIV-ve MSM (45+), UK born, PHE region D 2 samples, collected in the same month and year Both samples are sublineage 1, and identical (0 pwSNPs) Likely the same infection (depending on dates, treatment, etc), but can’t rule out reinfection with same strain.
Patient 3 HIV-ve MSM (35-44), not UK born, based in London (C) 2 samples, collected 9 months apart Both samples are sublineage 1, but have 7 pairwise SNPs between them (loads!) Reinfection – probably from a different transmission network
However, based on the sample dates, as well as the outcome of the downstream genetic analysis, we can see that Patient 3 has duplicate infection events (different dates, 10 months apart) and the genomes are distinct (7 SNPs apart), whereas Patient 4 samples were collected in the same month and year (i.e. are likely duplicates from the same infection) and has identical genomes.
For downstream analysis purposes, we will retain both samples for Patient 3 (discrete infections), but exclude one sample from Patient 4 (duplicate infection samples) - ‘PHE150126A’ has much better genome coverage, so exclude ‘PHE150125A’
### Further Exclusions
PHE130056A - duplicate of PHE130057B (already removed, so not relevant) - don’t exclude! PHE170402A - quality control sample PHE170378A - quality control sample
Exclude duplicate sequences
duplicate.exclusion.list <- c("PHE150125A","PHE170402A","PHE170378A")
PHE.metadata.linked <- PHE.metadata.linked[PHE.metadata.linked$Sample_Name %notin% duplicate.exclusion.list,]
Moving on…
Define some colour schemes
# define some colors for each region
PHE.region.cols.brew <- data.frame(UKHSA.region=c("North East", "North West", "Yorkshire and Humber", "East Midlands", "West Midlands", "East of England", "London", "South East","South West","UK (not England)", "Not Known"), stringsAsFactors=F)
PHE.region.cols.brew$region.col <- c("#A6CEE3","#1F78B4","#CAB2D6","#33A02C","#B2DF8A","#FF7F00","#E31A1C","#FB9A99","#D4BB02","grey75","grey25")
# HIV color scheme
PHE.hiv.cols <- data.frame(hivpos=rev(sort(unique(PHE.metadata.linked$hivpos))), stringsAsFactors=F)
PHE.hiv.cols$hiv.cols <- c("#1f78b4","#b2df8a","grey75")
# Orientation colour scheme
PHE.orientation.cols <- data.frame(orientation=rev(sort(unique(PHE.metadata.linked$gender_orientation))), stringsAsFactors=F)
PHE.orientation.cols$orientation <- factor(PHE.orientation.cols$orientation, levels=rev(sort(unique(PHE.metadata.linked$gender_orientation))), labels=c("MSW","GBMSM","WSM","MUnknown","Unknown"))
PHE.orientation.cols$orientation.cols <- c("#1f78b4","#b2df8a","#fb9a99","#a6cee3","grey75")
# UK born colour scheme
PHE.ukborn.cols <- data.frame(ukborn=rev(sort(unique(PHE.metadata.linked$ukborn))),ukborn.cols=c("#1f78b4","#b2df8a","grey75"),stringsAsFactors = F)
# London based colour scheme
PHE.london.cols <- data.frame(london=rev(sort(unique(PHE.metadata.linked$london))),london.cols=c("#1f78b4","#b2df8a","grey75"),stringsAsFactors = F)
# Age group colour scheme
PHE.Age.cols <- data.frame(age_group=rev(sort(unique(PHE.metadata.linked$age_group))),stringsAsFactors = T)
PHE.Age.cols$age_group.cols <- c(brewer.pal(n=4,"YlGnBu"),"grey75")
# Sample Date colour scheme
PHE.year.cols <- data.frame(year=(sort(unique(PHE.metadata.linked$year))),stringsAsFactors = T)
PHE.year.cols$year.cols <- brewer.pal(n=7,"YlOrRd")
# Sample Date (all global data, but with 1990 cuttoff)
TPA.year.cuttoff.cols <- data.frame(date.cuttoff=c("<1999",1999:2019), date.cuttoff.col=c("#F2F2F2",colorRampPalette(brewer.pal(7, "YlOrRd"))(length(1999:2019))))
##### ## First describe the sequenced population as a whole
Set order of PHE regions
PHE.metadata.linked$phe_centre <- factor(PHE.metadata.linked$phe_centre, levels=rev(PHE.region.cols.brew$UKHSA.region))
Generate some basic statistics about geographical PHE regions (anonymised)
PHE.count.all <- PHE.metadata.linked %>%
dplyr::summarise(count.per.region=n())
PHE.count.years <- PHE.metadata.linked %>%
dplyr::group_by(year) %>%
dplyr::summarise(count.per.year=n()) %>%
ungroup() %>%
dplyr::mutate(perc.per.year=(count.per.year/sum(count.per.year))*100)
# Generate some stats about HIV status
PHE.HIV.counts <- PHE.metadata.linked %>%
dplyr::group_by(hivpos) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::mutate(fraction=Count/total.region) %>%
dplyr::arrange(desc(hivpos), .by_group=T) %>%
dplyr::mutate(cum_fract = cumsum(fraction)) %>%
dplyr::mutate(cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(HIV.perc=(Count/sum(Count)*100))
# Generate some stats about gender orientation
PHE.orientation.counts <- PHE.metadata.linked %>%
dplyr::group_by(gender_orientation) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(gender_orientation), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(orientation.perc=(Count/sum(Count)*100))
# Generate some stats about UK born (vague category that's unfortunately only marginally helpful)
PHE.UKborn.counts <- PHE.metadata.linked %>%
dplyr::group_by(ukborn) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(ukborn), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(UKborn.perc=(Count/sum(Count)*100))
# Generate some stats about London based
PHE.London.counts <- PHE.metadata.linked %>%
dplyr::group_by(london) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(london), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(London.perc=(Count/sum(Count)*100))
# Generate some stats about Age group
PHE.Age.counts <- PHE.metadata.linked %>%
dplyr::group_by(age_group) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(age_group), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(Age.perc=(Count/sum(Count)*100))
# Generate some stats about Lineage group
PHE.Lineage.counts <- PHE.metadata.linked %>%
dplyr::group_by(TPA_Lineage) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(TPA_Lineage), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(Lineage.perc=(Count/sum(Count)*100))
# Generate some stats about sublineage group
PHE.sublineage.counts <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(TPA.pinecone.sublineage), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(Sublineage.perc=(Count/sum(Count)*100))
Make some plots
# Make hbar plot of sample counts by region
p.all.hbarplot <- ggplot(PHE.count.all, aes(x=count.per.region,y="")) +
geom_barh(stat="identity", position="stack", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
scale_fill_manual(values="grey30") +
geom_text(data=PHE.count.all, aes((count.per.region+12), "",label=count.per.region), size=theme.text.size.within, inherit.aes = F) +
labs(y="All", x="Sample Count") +
coord_cartesian(xlim=c(0,260)) +
guides(fill=guide_legend(nrow=4))
#p.all.hbarplot
# make temporal bubbleplot of counts by region
p.all.year.bubbleplot <- ggplot(PHE.count.years, aes(as.numeric(year), y="All")) +
geom_point(alpha=0.65, aes(size=count.per.year)) +
geom_line(alpha=0.25) +
guides(colour='none') +
scale_size_area(max_size = 7,breaks=c(1,5,10,25,50)) +
guides(size=guide_legend(nrow=2)) +
theme_light() +
scale_fill_manual(values="grey30") +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
labs(y="", x="Sample Year", size="Count")
#p.all.year.bubbleplot
# Make proportional hbar plot of HIV status
p.all.hiv.hbarplot <- ggplot(PHE.HIV.counts, aes(Count,y="",fill=hivpos)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
scale_fill_manual(name="HIV +ve",values=PHE.hiv.cols$hiv.cols, breaks=PHE.hiv.cols$hivpos) +
labs(y="All", x="HIV +ve") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.HIV.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F) +
NULL
#p.all.hiv.hbarplot
p.all.orientation.hbarplot <- ggplot(PHE.orientation.counts, aes(Count,y="",fill=gender_orientation)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
scale_fill_manual(name="Orientation",values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
labs(y="All", x="Orientation") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.orientation.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F)
#p.all.orientation.hbarplot
p.all.ukborn.hbarplot <- ggplot(PHE.UKborn.counts, aes(Count,y="",fill=ukborn)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
scale_fill_manual(name="UK\nBorn",values=PHE.ukborn.cols$ukborn.cols, breaks=PHE.ukborn.cols$ukborn) +
labs(y="All", x="UK Born") +
#guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.UKborn.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F)
#p.all.ukborn.hbarplot
p.all.London.hbarplot <- ggplot(PHE.London.counts, aes(Count,y="",fill=london)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
scale_fill_manual(name="London",values=PHE.london.cols$london.cols, breaks=PHE.london.cols$london) +
labs(y="All", x="London") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.London.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F)
#p.all.London.hbarplot
p.all.Age.hbarplot <- ggplot(PHE.Age.counts, aes(Count,y="",fill=age_group)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
scale_fill_manual(name="Age\nGroup",values=PHE.Age.cols$age_group.cols, breaks=PHE.Age.cols$age_group) +
labs(y="All", x="Age Group") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.Age.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F)
#p.all.Age.hbarplot
Plot combined plot for ‘all samples’
PHE.all.combiplot.1 <- plot_grid(p.all.year.bubbleplot, p.all.hbarplot + y.theme.strip, p.all.orientation.hbarplot + y.theme.strip, p.all.hiv.hbarplot + y.theme.strip, p.all.Age.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(4,2,2,2,2), scale=0.9)
PHE.all.combiplot.1

Next just describe population distributions by PHE region
# generate some basic statistics about geographical PHE regions (anonymised)
PHE.geo.count <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre) %>%
dplyr::summarise(count.per.region=n()) %>%
dplyr::mutate(total.count=sum(count.per.region),fraction=count.per.region/total.count)
PHE.geo.count.years <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre,year) %>%
dplyr::summarise(count.per.region.year=n())
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
PHE.geo.count.years.lineage <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre,year,TPA_Lineage) %>%
dplyr::summarise(count.per.region.year=n()) %>%
dplyr::mutate(total.count.year=sum(count.per.region.year)) %>%
dplyr::ungroup() %>%
tidyr::pivot_wider(names_from=TPA_Lineage, values_from = count.per.region.year)
`summarise()` has grouped output by 'phe_centre', 'year'. You can override using the `.groups` argument.
PHE.geo.count.years.lineage[is.na(PHE.geo.count.years.lineage)] <- 0
PHE.geo.count.years.lineage$year <- as.numeric(PHE.geo.count.years.lineage$year)
# Generate some stats about HIV status
PHE.geo.HIV.counts <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre,hivpos) %>%
dplyr::summarise(count.per.region.hiv=n()) %>%
dplyr::mutate(total.region=sum(count.per.region.hiv)) %>%
dplyr::mutate(fraction=count.per.region.hiv/total.region) %>%
dplyr::arrange(desc(hivpos), .by_group=T) %>%
dplyr::mutate(cum_fract = cumsum(fraction)) %>%
dplyr::mutate(cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Double Check HIV status data for non-PHE dataset - confirmed no HIV+ves from non-MSM.
PHE.sourcelab.HIV.counts <- PHE.metadata.linked %>%
dplyr::group_by(is.PHE, gender_orientation, hivpos) %>%
dplyr::summarise(count.per.orientation.hiv=n()) #%>%
`summarise()` has grouped output by 'is.PHE', 'gender_orientation'. You can override using the `.groups` argument.
#dplyr::filter(is.PHE!="PHE")
# Get total population stats for HIV
PHE.all.HIV.counts <- PHE.metadata.linked %>%
dplyr::group_by(hivpos) %>%
dplyr::summarise(count.hiv=n()) %>%
dplyr::mutate(count.total=sum(count.hiv), fraction=count.hiv/count.total)
# Generate some stats about gender orientation
PHE.orientation.counts <- PHE.metadata.linked %>%
dplyr::group_by(gender_orientation) %>%
dplyr::summarise(orientation.count=n()) %>%
dplyr::mutate(orientation.percent=(orientation.count/sum(orientation.count)*100))
PHE.geo.orientation.counts <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre,gender_orientation) %>%
dplyr::summarise(count.per.region.orientation=n()) %>%
dplyr::mutate(total.region=sum(count.per.region.orientation)) %>%
dplyr::arrange(desc(gender_orientation), .by_group=T) %>%
dplyr::mutate(fraction=count.per.region.orientation/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(orientation.percent=(count.per.region.orientation/sum(count.per.region.orientation)*100))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about UK born
PHE.geo.UKborn <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre, ukborn) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(ukborn), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about London based
PHE.geo.London <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre, london) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(london), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about Age group
PHE.geo.Age <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre, age_group) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(age_group), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about Lineage group
PHE.geo.Lineage <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre, TPA_Lineage) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(TPA_Lineage), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about sublineage group
PHE.geo.sublineage <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre, TPA.pinecone.sublineage) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(TPA.pinecone.sublineage), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
Make some plots
# Make hbar plot of sample counts by region
p.region.hbarplot <- ggplot(PHE.geo.count, aes(count.per.region,phe_centre, fill=phe_centre)) +
geom_barh(stat="identity", position="stack", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="UKHSA\nRegion",values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region) +
geom_text(data=PHE.geo.count, aes((count.per.region+12), phe_centre,label=count.per.region), size=theme.text.size.within, inherit.aes = F) +
labs(y="UKHSA Region", x="Sample Count") +
#coord_cartesian(xlim=c(0,130)) +
coord_cartesian(xlim=c(0,260)) +
guides(fill=guide_legend(ncol=2))
#p.region.hbarplot
# make temporal bubbleplot of counts by region
p.region.year.bubbleplot <- ggplot(PHE.geo.count.years, aes(as.numeric(year), phe_centre, colour=phe_centre)) +
geom_point(alpha=0.65, aes(size=count.per.region.year)) +
geom_line(alpha=0.25) +
guides(colour='none') +
scale_size_area(max_size = 7,breaks=c(1,5,10,25,50)) +
guides(size=guide_legend(nrow=2, direction = 'horizontal', byrow=T)) +
theme_light() +
scale_color_manual(name="UKHSA\nRegion",values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
labs(y="UKHSA Region", x="Sample Year", size="Count")
#p.region.year.bubbleplot
# Or a barplot of lineage by year & PHE region?
p.region.year.bubbleplot.barplot.facet.lineage <- PHE.geo.count.years.lineage %>% tidyr::pivot_longer(c(SS14, Nichols), names_to="TPA_Lineage", values_to="Count") %>%
ggplot(aes(year, Count, fill=TPA_Lineage)) +
geom_bar(stat='identity', width=0.6) +
facet_grid(phe_centre~., scales='free') +
guides(size=guide_legend(nrow=2)) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="TPA\nLineage",values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage) +
theme(strip.background = element_rect(color='white', fill='white',linetype="solid"), strip.text.y = element_text(color = "grey25", size=7, angle=0))
#p.region.year.bubbleplot.barplot.facet.lineage
# Make proportional hbar plot of HIV status
p.region.hiv.hbarplot <- ggplot(PHE.geo.HIV.counts, aes(count.per.region.hiv,phe_centre,fill=hivpos)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="HIV +ve",values=PHE.hiv.cols$hiv.cols, breaks=PHE.hiv.cols$hivpos) +
labs(y="UKHSA Region", x="HIV +ve") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.geo.HIV.counts, aes(cum_fract.mid, phe_centre,label=count.per.region.hiv), size=theme.text.size.within, inherit.aes = F) +
NULL
#p.region.hiv.hbarplot
p.region.orientation.hbarplot <- ggplot(PHE.geo.orientation.counts, aes(count.per.region.orientation,phe_centre,fill=gender_orientation)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="Orientation",values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
labs(y="UKHSA Region", x="Orientation") +
guides(fill=guide_legend(ncol=1)) +
geom_text(data=PHE.geo.orientation.counts, aes(cum_fract.mid, phe_centre,label=count.per.region.orientation), size=theme.text.size.within, inherit.aes = F)
#p.region.orientation.hbarplot
p.region.ukborn.hbarplot <- ggplot(PHE.geo.UKborn, aes(Count,phe_centre,fill=ukborn)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="UK Born",values=PHE.ukborn.cols$ukborn.cols, breaks=PHE.ukborn.cols$ukborn) +
labs(y="UKHSA Region", x="UK Born") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.geo.UKborn, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.region.ukborn.hbarplot
p.region.London.hbarplot <- ggplot(PHE.geo.London, aes(Count,phe_centre,fill=london)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="London",values=PHE.london.cols$london.cols, breaks=PHE.london.cols$london) +
labs(y="UKHSA Region", x="London") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.geo.London, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.region.London.hbarplot
p.region.Age.hbarplot <- ggplot(PHE.geo.Age, aes(Count,phe_centre,fill=age_group)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="Age\nGroup",values=PHE.Age.cols$age_group.cols, breaks=PHE.Age.cols$age_group) +
labs(y="UKHSA Region", x="Age Group") +
guides(fill=guide_legend(ncol=1)) +
geom_text(data=PHE.geo.Age, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.region.Age.hbarplot
Combined plot
PHE.region.combiplot.1 <- plot_grid(p.region.year.bubbleplot, p.region.hbarplot + y.theme.strip, p.region.orientation.hbarplot + y.theme.strip, p.region.hiv.hbarplot + y.theme.strip, p.region.Age.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(4,2,2,2,2), scale=0.9)
PHE.region.combiplot.1

Regions as a complex multipanel plot
# legends
PHE.region.combiplot.1.legends <- plot_grid(get_legend(p.region.year.bubbleplot), get_legend(p.region.hbarplot + y.theme.strip), get_legend(p.region.orientation.hbarplot + y.theme.strip), get_legend(p.region.hiv.hbarplot + y.theme.strip), get_legend(p.region.Age.hbarplot + y.theme.strip), nrow=1, align="h", rel_widths=c(6,4,4,4,4), scale=0.95)
# Arrange plots vertically
p.year.bubbleplot.combi <- plot_grid(p.all.year.bubbleplot + x.theme.strip, p.region.year.bubbleplot + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.region.hbar.counts.combi <- plot_grid(p.all.hbarplot + x.theme.strip + y.theme.strip, p.region.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.region.hbar.orientation.combi <- plot_grid(p.all.orientation.hbarplot + x.theme.strip + y.theme.strip, p.region.orientation.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.region.hbar.hiv.combi <- plot_grid(p.all.hiv.hbarplot + x.theme.strip + y.theme.strip, p.region.hiv.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.region.hbar.Age.combi <- plot_grid(p.all.Age.hbarplot + x.theme.strip + y.theme.strip, p.region.Age.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
# Combine the plots
p.region.hbar.combi.plus.all <- plot_grid(p.year.bubbleplot.combi, p.region.hbar.counts.combi, p.region.hbar.orientation.combi, p.region.hbar.hiv.combi, p.region.hbar.Age.combi, nrow=1, rel_widths=c(6,4,4,4,4), labels = c("A","B","C","D","E"), label_size=panel.lab.size, vjust=0.25)
# and add the legends on top
p.region.hbar.combi.plus.all.with.legends <- plot_grid(p.region.hbar.combi.plus.all, PHE.region.combiplot.1.legends, ncol=1, rel_heights=c(6,1), scale = 0.95)
p.region.hbar.combi.plus.all.with.legends

#ggsave(paste0(Figure_output_directory, "SupFig2_TPA-PHE_Sample-metadistros-by-phe_region+all-combi.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=240, height=135, device='pdf', dpi=1200)
Now lets look at some genetic data
### Make ML tree with sublineage tippoints
TPA.MLtree.ggtree.tippoint <- TPA.MLtree.ggtree %<+% data.frame(Sample_Name=TPA.meta2.1$Sample_Name, Sublineage=TPA.meta2.1$TPA.pinecone.sublineage,stringsAsFactors = F) +
geom_tippoint(aes(color=Sublineage), size=0.5, alpha=0.5, show.legend = FALSE) +
scale_color_manual(name="Sublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage)
Add metadata
# Continent
p.TPA.MLtree.PHE <- gheatmap(TPA.MLtree.ggtree.tippoint,
TPA.rawseq.continents.p, color=NULL,width=0.075,offset=0.00000025, colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) +
scale_fill_manual(name="Continent",values=continental.cols.brew2$continent.col, breaks=continental.cols.brew2$Continent, guide = guide_legend(order = 1,ncol=2)) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# is UK
p.TPA.MLtree.PHE <- gheatmap(p.TPA.MLtree.PHE,
TPA.rawseq.UK.p, color=NULL,width=0.075,offset=0.00001025, colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) +
scale_fill_manual(name="England/Other", values=c("black","grey95"), breaks=c("England","Other"), guide = guide_legend(order = 2,ncol=2)) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# Lineage
p.TPA.MLtree.PHE <- gheatmap(p.TPA.MLtree.PHE,TPA.rawseq.Lineage.p, color=NULL,width=0.075,offset=0.00002025, colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) +
scale_fill_manual(name="Lineage",values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage, guide = guide_legend(order = 3, ncol=2)) + theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
new_scale_fill() +
NULL
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# sublineage
p.TPA.MLtree.PHE <- gheatmap(p.TPA.MLtree.PHE, data.frame(row.names=TPA.meta2.1$Sample_Name, Sublineage=TPA.meta2.1$TPA.pinecone.sublineage,stringsAsFactors = F), color=NULL,width=0.075,offset=0.00003025, colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) +
scale_fill_manual(name="Sublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage, guide = guide_legend(order = 4, ncol=3)) + theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
new_scale_fill() +
NULL
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
plot
p.TPA.MLtree.PHE

#ggsave(paste0(Figure_output_directory, "SupFig3_TPA-PHE_Global_Phylo+UK-highlights.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=185, height=160, device='pdf', dpi=1200)
### Geographic distributions of Lineages and Sublineages What about sublineages?
p.region.Lineage.hbarplot <- ggplot(PHE.geo.Lineage, aes(Count,phe_centre,fill=TPA_Lineage)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="TPA\nLineage",values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage) +
labs(y="UKHSA Region", x="TPA Lineage") +
guides(fill=guide_legend(nrow=3)) +
#geom_text(data=PHE.geo.Lineage, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F) +
NULL
p.region.sublineage.hbarplot <- ggplot(PHE.geo.sublineage, aes(Count,phe_centre,fill=TPA.pinecone.sublineage)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="TPA\nSublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage) +
labs(y="UKHSA Region", x="TPA Sublineage") +
guides(fill=guide_legend(nrow=4)) +
#geom_text(data=PHE.geo.sublineage, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F) +
NULL
Combi plot (geography lineages)
PHE.region.combiplot.2.lineages <- plot_grid(p.region.year.bubbleplot +legend.strip, p.region.hbarplot + y.theme.strip + legend.strip + coord_cartesian(xlim=c(0,150)), p.region.Lineage.hbarplot + y.theme.strip +legend.strip, p.region.sublineage.hbarplot + y.theme.strip +legend.strip, nrow=1, align="h", rel_widths=c(6,3,4,4), scale=0.99, labels=c("C","D","E","F"), label_size=panel.lab.size)
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
# separate out the plot for the legends
p.region.year.bubbleplot.legend <- get_legend(p.region.year.bubbleplot)
p.region.hbarplot.legend <- get_legend(p.region.hbarplot + y.theme.strip)
p.region.Lineage.hbarplot.legend <- get_legend(p.region.Lineage.hbarplot + y.theme.strip)
p.region.sublineage.hbarplot.legend <- get_legend(p.region.sublineage.hbarplot + y.theme.strip)
PHE.region.combiplot.2.lineages.legend <- plot_grid(p.region.year.bubbleplot.legend, p.region.hbarplot.legend, p.region.Lineage.hbarplot.legend, p.region.sublineage.hbarplot.legend, nrow=1, align="h", rel_widths=c(6,3,4,4))
PHE.region.combiplot.2.lineages <- plot_grid(PHE.region.combiplot.2.lineages, PHE.region.combiplot.2.lineages.legend, rel_heights = c(4,1), ncol=1)
PHE.region.combiplot.2.lineages

OK, let’s now add a map of these geographical distributions
Let’s used ONS published shape files - there is one available that shows Public Health England region boundaries.
# Generate approximate regional GPS coords
PHE.region.GPS <- data.frame(
stringsAsFactors = FALSE,
phe_centre = c("East Midlands",
"East of England","London","North East","North West",
"South East","South West","West Midlands",
"Yorkshire and Humber","UK (not England)","Not Known"),
Longitude = c(-0.7,0.5,-0.2,-1.9,-2.4,
0.05,-2.9,-2,-0.8,0.1,0.63),
Latitude = c(52.9,52.4,51.5,55,53.7,
51.1,51,52.6,53.8,54.7,54.1)
)
PHE.region.GPS <- left_join(PHE.region.GPS, PHE.geo.Lineage[PHE.geo.Lineage$TPA_Lineage=="SS14",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS)[4] <- "SS14"
PHE.region.GPS <- left_join(PHE.region.GPS, PHE.geo.Lineage[PHE.geo.Lineage$TPA_Lineage=="Nichols",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS)[5] <- "Nichols"
PHE.region.GPS[is.na(PHE.region.GPS)] <- 0
PHE.region.GPS <- left_join(PHE.region.GPS, PHE.geo.Lineage[PHE.geo.Lineage$TPA_Lineage=="SS14",c("phe_centre","total.region")], by="phe_centre")
colnames(PHE.region.GPS)[6] <- "Region_Count"
PHE.region.GPS$radius <- 0.5*(1-1/sqrt(PHE.region.GPS$Region_Count))
###############################
# Import datafile from https://geoportal.statistics.gov.uk/datasets/public-health-england-centres-december-2016-full-clipped-boundaries-in-england/explore?location=52.950000%2C-2.000000%2C6.88
UK.shapefile <- readOGR(dsn=UK.publichealth.shapefile.data)
Warning in OGRSpatialRef(dsn, layer, morphFromESRI = morphFromESRI, dumpSRS = dumpSRS, :
Discarded datum Ordnance_Survey_of_Great_Britain_1936 in Proj4 definition: +proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +units=m +no_defs
OGR data source with driver: ESRI Shapefile
Source: "/Users/mb29/Papers/Treponema_UK-PHE-gen-epi_2021/Rnotebook/Rnotebook_09-2022/inputdata/Public_Health_England_Centres_(December_2016)_Boundaries", layer: "Public_Health_England_Centres_(December_2016)_Boundaries"
with 9 features
It has 9 fields
#Reshape for ggplot2 using the Broom package
UK.mapdata <- tidy(UK.shapefile, region="phec16nm")
#UK.gg <- ggplot() + geom_polygon(data = UK.mapdata, aes(x = long, y = lat, group = group), color = "#FFFFFF", size = 0.25)
UK.gg <- ggplot() + geom_polygon(data = UK.mapdata, aes(x = long, y = lat, group = group), color="grey25", fill="grey90", size = 0.075)
UK.gg <- UK.gg + coord_fixed(1) + theme_nothing()
#UK.gg
# Convert UK regions to be compatible with map
# First find centre point for each region
UK.mapdata.regions.meancoords <- UK.mapdata %>% dplyr::group_by(id) %>%
dplyr::summarise(mean.lat=mean(lat), mean.long=median(long)) %>%
dplyr::ungroup()
colnames(UK.mapdata.regions.meancoords)[1] <- "phe_centre"
PHE.region.GPS.ukmap <- dplyr::left_join(PHE.region.GPS, UK.mapdata.regions.meancoords, by="phe_centre")
# Add artificial location for 'not known'
PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre=="Not Known","mean.lat"] <- 600000
PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre=="Not Known","mean.long"] <- 550000
# Shift "South East" slightly to reduce the overlap with London
PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre=="South East","mean.long"] <- 475000
# Shift "East of England East" slightly to reduce the overlap with London
PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre=="East of England","mean.lat"] <- 275000
# Not going to try plotting the 2 samples from elsewhere in the UK, so remove that row
PHE.region.GPS.ukmap <- PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre != "UK (not England)",]
# Create radius variable for plotting pie sizes (use log10(n)*20,000)
PHE.region.GPS.ukmap$radius.UK <- log10(PHE.region.GPS.ukmap$Region_Count)*20000
#PHE.geo.count.years.lineage
UK.gg.scatterpie <- UK.gg + geom_scatterpie(data=PHE.region.GPS.ukmap, aes(mean.long, mean.lat, group=phe_centre, r=radius.UK), alpha=0.85, color=NA, cols=c("Nichols","SS14")) +
scale_fill_manual(name="TPA\nLineage",values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage) + theme(legend.position="top")
UK.gg.scatterpie <- UK.gg.scatterpie + geom_scatterpie_legend(PHE.region.GPS.ukmap[!is.na(PHE.region.GPS.ukmap$mean.lat),"radius.UK"], labeller=function(x) round((10^(x/20000)),0), n=3, x=150000, y=500000)
UK.gg.scatterpie <- UK.gg.scatterpie + theme_nothing()
#? Add labels
UK.gg.scatterpie.labs <- UK.gg.scatterpie + geom_label_repel(data=PHE.region.GPS.ukmap[!is.na(PHE.region.GPS.ukmap$mean.lat),], aes(mean.long, mean.lat, label=phe_centre), size=theme.text.size.within, nudge_x = 50000, nudge_y = -25000, segment.size = 0.1) + theme(legend.key.size = unit(0.55,"line"), legend.position="bottom") +
theme.text.size +
theme_nothing()
UK.gg.scatterpie.labs

Now do an equivalent plot for sublineages
PHE.region.GPS.ukmap.sublin <- PHE.region.GPS.ukmap
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="1",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[11] <- "1"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="2",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[12] <- "2"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="3",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[13] <- "3"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="6",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[14] <- "6"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="8",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[15] <- "8"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="14",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[16] <- "14"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="15",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[17] <- "15"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="16",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[18] <- "16"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="Singleton",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[19] <- "Singleton"
PHE.region.GPS.ukmap.sublin[is.na(PHE.region.GPS.ukmap.sublin)] <- 0
# Most samples are either sublineage 1 or 14. Let's create a count of samples that are neither.
PHE.region.GPS.ukmap.sublin$`Other Sublineages` <- sapply(1:nrow(PHE.region.GPS.ukmap.sublin), function (x) PHE.region.GPS.ukmap.sublin$Region_Count[x]-sum(PHE.region.GPS.ukmap.sublin$`1`[x], PHE.region.GPS.ukmap.sublin$`14`[x]))
UK.gg.scatterpie.sublineage <- UK.gg + geom_scatterpie(data=PHE.region.GPS.ukmap.sublin[PHE.region.GPS.ukmap.sublin$mean.long!=0,], aes(mean.long, mean.lat, group=phe_centre, r=radius.UK), alpha=0.85, color=NA, cols=c("1","14","Other Sublineages")) +
scale_fill_manual(name="TPA\nSublineage",values=c("#FC9272","#BCBDDC", "grey50"), breaks=c("1","14","Other Sublineages"))
# add legend
UK.gg.scatterpie.sublineage <- UK.gg.scatterpie.sublineage + geom_scatterpie_legend(PHE.region.GPS.ukmap[!is.na(PHE.region.GPS.ukmap$mean.lat),"radius.UK"], labeller=function(x) round((10^(x/20000)),0), n=3, x=150000, y=500000)
#UK.gg.scatterpie <- UK.gg.scatterpie + x.theme.strip + y.theme.strip
UK.gg.scatterpie.sublineage <- UK.gg.scatterpie.sublineage + theme_nothing()
#? Add labels
UK.gg.scatterpie.sublineage <- UK.gg.scatterpie.sublineage + geom_label_repel(data=PHE.region.GPS.ukmap[!is.na(PHE.region.GPS.ukmap$mean.lat),], aes(mean.long, mean.lat, label=phe_centre), size=theme.text.size.within, nudge_x = 50000, nudge_y = -25000, segment.size = 0.1) +
theme(legend.key.size = unit(0.55,"line"), legend.position="bottom") +
theme.text.size +
theme_nothing()
UK.gg.scatterpie.sublineage

Combined map plot
UK.gg.scatterpie.combi <- plot_grid(UK.gg.scatterpie.labs, UK.gg.scatterpie.sublineage, ncol=2, labels = c("A","B"), label_size=panel.lab.size)
UK.gg.scatterpie.combi

Plot in combination with barplots
plot_grid(UK.gg.scatterpie.combi, PHE.region.combiplot.2.lineages, nrow=2, rel_heights=c(4,5))

#ggsave(paste0(Figure_output_directory,"Fig2_TPA-PHE_Map-Lineage+Barplots.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=190, height=185, device='pdf', dpi=1200)
### Analysis by sublineage
Now lets start exploring how samples are distributed by sublineage
PHE.metadata.linked <- PHE.metadata.linked
PHE.metadata.linked$TPA.pinecone.sublineage <- factor(PHE.metadata.linked$TPA.pinecone.sublineage, levels=rev(as.character(sort(unique(PHE.metadata.linked$TPA.pinecone.sublineage)))))
PHE.Lineage.count <- PHE.metadata.linked %>%
dplyr::group_by(TPA_Lineage) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total=sum(Count), perc=(Count/total)*100)
PHE.sublin.count <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total=sum(Count), perc=(Count/total)*100)
PHE.geo.sublin.years <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage,year) %>%
dplyr::summarise(Count=n())
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
## Generate some stats about sublineage groups
# Generate some stats about gender orientation
PHE.sublineage.orientation.counts <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage,gender_orientation) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
dplyr::arrange(desc(gender_orientation), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Generate some stats about UK born
PHE.sublineage.UKborn <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage, ukborn) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
#dplyr::arrange(desc(ukborn), .by_group=T) %>%
dplyr::arrange(desc(ukborn), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Generate some stats about London based
PHE.sublineage.London <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage, london) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
dplyr::arrange(desc(london), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Generate some stats about Age group
PHE.sublineage.Age <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage, age_group) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
dplyr::arrange(desc(age_group), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Generate some stats about HIV group
PHE.sublineage.HIV <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage, hivpos) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
dplyr::arrange(desc(hivpos), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Generate some stats by PHE Region
PHE.sublineage.PHEcentre <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage, phe_centre) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
dplyr::arrange(desc(phe_centre), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
Plot by sublineage
p.sublineage.year.bubbleplot <- ggplot(PHE.geo.sublin.years, aes(as.numeric(year), TPA.pinecone.sublineage, colour=TPA.pinecone.sublineage)) +
geom_point(alpha=0.65, aes(size=Count)) +
geom_line(alpha=0.25) +
guides(colour='none') +
scale_size_area(max_size = 7,breaks=c(1,5,10,25,50)) +
guides(size=guide_legend(nrow=2, direction = 'horizontal', byrow=T)) +
theme_light() +
scale_color_manual(name="TPA\nSublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
labs(y="TPA Sublineage", x="Sample Year", size="Count")
#p.sublineage.year.bubbleplot
p.sublineage.hbarplot <- ggplot(PHE.sublin.count, aes(Count,TPA.pinecone.sublineage,fill=TPA.pinecone.sublineage)) +
geom_barh(stat="identity", position="stack", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="TPA\nSublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage) +
labs(y="TPA Sublineage", x="Sample Count") +
geom_text(data=PHE.sublin.count, aes((Count+12), TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F) +
#coord_cartesian(xlim=c(0,200)) +
coord_cartesian(xlim=c(0,260)) +
guides(fill=guide_legend(ncol=2))
#p.sublineage.hbarplot
p.sublineage.orientation.hbarplot <- ggplot(PHE.sublineage.orientation.counts, aes(y=TPA.pinecone.sublineage,x=Count,fill=gender_orientation)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="Orientation",values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
labs(y="TPA Sublineage", x="Orientation") +
guides(fill=guide_legend(ncol=1)) +
geom_text(data=PHE.sublineage.orientation.counts, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.region.orientation.hbarplot
p.sublineage.hiv.hbarplot <- ggplot(PHE.sublineage.HIV, aes(y=TPA.pinecone.sublineage, x=Count,fill=hivpos)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="HIV +ve",values=PHE.hiv.cols$hiv.cols, breaks=PHE.hiv.cols$hivpos) +
labs(y="TPA Sublineage", x="HIV +ve") +
guides(fill=guide_legend(ncol=1)) +
geom_text(data=PHE.sublineage.HIV, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.sublineage.hiv.hbarplot
p.sublineage.ukborn.hbarplot <- ggplot(PHE.sublineage.UKborn, aes(y=TPA.pinecone.sublineage,x=Count,fill=ukborn)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="UK\nborn",values=PHE.ukborn.cols$ukborn.cols, breaks=PHE.ukborn.cols$ukborn) +
labs(y="TPA Sublineage", x="UK born") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.sublineage.UKborn, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.sublineage.ukborn.hbarplot
p.sublineage.Age.hbarplot <- ggplot(PHE.sublineage.Age, aes(y=TPA.pinecone.sublineage, x=Count ,fill=age_group)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="Age\nGroup",values=PHE.Age.cols$age_group.cols, breaks=PHE.Age.cols$age_group) +
labs(y="TPA Sublineage", x="Age Group") +
guides(fill=guide_legend(ncol=1)) +
geom_text(data=PHE.sublineage.Age, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.sublineage.Age.hbarplot
p.sublineage.PHEregion.hbarplot <- ggplot(PHE.sublineage.PHEcentre, aes(y=TPA.pinecone.sublineage, x=Count, fill=phe_centre)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="UKHSA\nRegion",values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$PHE.region) +
labs(y="TPA Sublineage", x="UKHSA Region") +
guides(fill=guide_legend(nrow=4)) +
geom_text(data=PHE.sublineage.PHEcentre, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
Look at how sublineages are distributed by region (sublineage-centric)
p.sublineage.PHEregion.hbarplot

Combine patient metadata into a plot
#PHE.sublineages.combiplot.1 <- plot_grid(p.sublineage.year.bubbleplot, p.sublineage.hbarplot + y.theme.strip, p.sublineage.orientation.hbarplot + y.theme.strip, p.sublineage.hiv.hbarplot + y.theme.strip, p.sublineage.PHEregion.hbarplot + y.theme.strip, p.sublineage.ukborn.hbarplot + y.theme.strip, p.sublineage.Age.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(3,2,2,2,2,2,2), scale=0.9)
#PHE.sublineages.combiplot.1 <- plot_grid(p.sublineage.year.bubbleplot, p.sublineage.hbarplot + y.theme.strip, p.sublineage.orientation.hbarplot + y.theme.strip, p.sublineage.hiv.hbarplot + y.theme.strip, p.sublineage.Age.hbarplot + y.theme.strip, p.sublineage.PHEregion.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(3,2,2,2,2,4), scale=0.9)
PHE.sublineages.combiplot.1 <- plot_grid(p.sublineage.year.bubbleplot, p.sublineage.hbarplot + y.theme.strip, p.sublineage.orientation.hbarplot + y.theme.strip, p.sublineage.hiv.hbarplot + y.theme.strip, p.sublineage.Age.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(4,2,2,2,2), scale=0.9)
PHE.sublineages.combiplot.1

Lets add the ‘all’ row again to the ‘by sublineage’ plot
# legends
PHE.sublineage.combiplot.1.legends <- plot_grid(get_legend(p.sublineage.year.bubbleplot), get_legend(p.sublineage.hbarplot + y.theme.strip), get_legend(p.sublineage.orientation.hbarplot + y.theme.strip), get_legend(p.sublineage.hiv.hbarplot + y.theme.strip), get_legend(p.sublineage.Age.hbarplot + y.theme.strip), nrow=1, align="h", rel_widths=c(6,4,4,4,4), scale=0.95)
# regions
#PHE.sublineage.combiplot.1.nolegend <- plot_grid(p.sublineage.year.bubbleplot + legend.strip, p.sublineage.hbarplot + y.theme.strip + legend.strip, p.sublineage.orientation.hbarplot + y.theme.strip + legend.strip, p.sublineage.hiv.hbarplot + y.theme.strip + legend.strip, p.sublineage.Age.hbarplot + y.theme.strip + legend.strip, nrow=1, align="h", rel_widths=c(4,2,2,2,2), scale=0.9)
# Or do it vertically
p.sublineage.year.bubbleplot.combi <- plot_grid(p.all.year.bubbleplot + x.theme.strip, p.sublineage.year.bubbleplot + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.sublineage.hbar.counts.combi <- plot_grid(p.all.hbarplot + x.theme.strip + y.theme.strip, p.sublineage.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.sublineage.hbar.orientation.combi <- plot_grid(p.all.orientation.hbarplot + x.theme.strip + y.theme.strip, p.sublineage.orientation.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.sublineage.hbar.hiv.combi <- plot_grid(p.all.hiv.hbarplot + x.theme.strip + y.theme.strip, p.sublineage.hiv.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.sublineage.hbar.Age.combi <- plot_grid(p.all.Age.hbarplot + x.theme.strip + y.theme.strip, p.sublineage.Age.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
# Combine the plots
p.sublineage.hbar.combi.plus.all <- plot_grid(p.sublineage.year.bubbleplot.combi, p.sublineage.hbar.counts.combi, p.sublineage.hbar.orientation.combi, p.sublineage.hbar.hiv.combi, p.sublineage.hbar.Age.combi, nrow=1, rel_widths=c(7,3,4,4,4), labels=c("A", "B", "C", "D", "E"),label_size=panel.lab.size, vjust=1, scale=0.99)
# and add the legends on top
#p.sublineage.hbar.combi.plus.all.with.legends <- plot_grid(PHE.sublineage.combiplot.1.legends, p.sublineage.hbar.combi.plus.all, ncol=1, rel_heights=c(1,9))
# legends below
p.sublineage.hbar.combi.plus.all.with.legends <- plot_grid(p.sublineage.hbar.combi.plus.all, PHE.sublineage.combiplot.1.legends, ncol=1, rel_heights=c(8,1))
p.sublineage.hbar.combi.plus.all.with.legends

These patterns look fairly similar between sublineages, and (apart from 1 & 14) the groups are very small. However, sublineage 14 does appear to have a higher proportion of MSM compared to sublineage 1 and others. Let’s test that formally using 2x2 fisher’s tests
PHE.MSM.counts.all <- PHE.metadata.linked %>%
dplyr::group_by(is.MSM, .drop=F) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
dplyr::arrange((is.MSM), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
PHE.sublineage.MSM.counts <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage,is.MSM, .drop=F) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
dplyr::arrange((is.MSM), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) #%>%
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
#dplyr::filter(!is.na(is.MSM))
PHE.sublineage.MSM.counts.wider <- PHE.sublineage.MSM.counts %>% dplyr::select(TPA.pinecone.sublineage, is.MSM, Count) %>%
tidyr::pivot_wider(names_from = is.MSM, values_from=Count) %>%
dplyr::mutate(MSM=replace_na(MSM, 0), Other=replace_na(Other, 0), Total=sum(MSM,Other)) %>%
#dplyr::select(-`NA`) %>%
dplyr::filter(Total!=0)
PHE.sublineage.MSM.pval <- data.frame(TPA.pinecone.sublineage=PHE.sublineage.MSM.counts.wider$TPA.pinecone.sublineage, p.fisher=sapply(1:nrow(PHE.sublineage.MSM.counts.wider), function (x) fisher.test(matrix(as.numeric(c(PHE.sublineage.MSM.counts.wider[x,"MSM"],
PHE.sublineage.MSM.counts.wider[x,"Other"],
PHE.MSM.counts.all[PHE.MSM.counts.all$is.MSM=="MSM","Count"], PHE.MSM.counts.all[PHE.MSM.counts.all$is.MSM=="Other","Count"])),nrow=2))[[1]]), stringsAsFactors=F)
PHE.sublineage.MSM.counts.wider <- dplyr::left_join(PHE.sublineage.MSM.counts.wider, PHE.sublineage.MSM.pval, by="TPA.pinecone.sublineage")
PHE.sublineage.MSM.counts.wider
### Visualisation of UK genomic relationships
Ok, let’s make a tree for displaying these relationships using the UK dataset only
From some experimentation, a ‘GrapeTree’ minimum spanning network works well for visualising the clonality of these populations. We can use a SNP-scaled phylogeny as direct input to GrapeTree, and this will allow branches to be scaled appropriately. However, although annotation is allowed within the GrapeTree software, colours must be manually edited. Final GrapeTree plots can then be imported back into R for combining with other plots.
Alternative visualisations - grapetree?
Take the 526-global phylogeny (snp-scaled version from pyjar), and prune to only include the UK strains from this study - this ensures the topology is consistent accross studies.
TPA.pyjar.tree.subset.uk <- ape::keep.tip(TPA.pyjar.tree, as.character(unlist(PHE.metadata.linked[PHE.metadata.linked$Geo_Country=="UK","Sample_Name"])))
ggtree(TPA.pyjar.tree.subset.uk)

#write.tree(TPA.pyjar.tree.subset.uk, paste0(Data_input_directory,"TPA.UK-only.pyjar.2022-02-03.tre"))
# Write out a metadata sheet for the relevant information
PHE.metadata.linked.grapetree <- PHE.metadata.linked[,c("Sample_Name", "year","gender_orientation","phe_centre","hivpos","ukborn","TPA_Lineage","TPA.pinecone.sublineage")]
colnames(PHE.metadata.linked.grapetree)[1] <- "ID"
#write.table(PHE.metadata.linked.grapetree, paste0(Data_input_directory,"TPA.UK-only.grapetree.meta.2022-02-03.tsv"), sep = "\t", quote=F, row.names = F)
Tree independently visualised and annotated using GrapeTree.
Now import and integrate GrapeTree plot with metadata plots.
# Combine the plots
p.sublineage.hbar.combi.plus.all.B2F <- plot_grid(p.sublineage.year.bubbleplot.combi, p.sublineage.hbar.counts.combi, p.sublineage.hbar.orientation.combi, p.sublineage.hbar.hiv.combi, p.sublineage.hbar.Age.combi, nrow=1, rel_widths=c(7,4,4,4,4), labels=c("B", "C", "D", "E", "F"),label_size=panel.lab.size, vjust=1, scale=0.97)
# legends below
p.sublineage.hbar.combi.plus.all.with.legends.B2F <- plot_grid(p.sublineage.hbar.combi.plus.all.B2F, PHE.sublineage.combiplot.1.legends, ncol=1, rel_heights=c(7,1))
#p.sublineage.hbar.combi.plus.all.with.legends.B2F
# Now bring in externally plotted Grapetree
p.TPA.UK.Grapetree.sublineages <- ggdraw() + draw_image(TPA.UK.Grapetree.sublineages.file)
p.TPA.UK.Grapetree.sublineages

p.sublineage.hbar.combi.plus.all.with.legends.B2F.with.grapetree <- plot_grid(p.TPA.UK.Grapetree.sublineages, p.sublineage.hbar.combi.plus.all.with.legends.B2F, ncol=1, labels=c("A",""), label_size=panel.lab.size, rel_heights=c(3,5))
p.sublineage.hbar.combi.plus.all.with.legends.B2F.with.grapetree

#ggsave(paste0(Figure_output_directory, "Fig1_TPA-PHE_Sample-distros-sublineage.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=190, height=185, device='pdf', dpi=1200)
Manage other GrapeTree plots (for consistency)
TPA-UK-2022-02-16.-MSTree_3-way-figure.Inscaped-2
# Bring in 3-way graphetree plot (3 different metadata variables using the same input tree)
TPA.UK.Grapetree.3way <- ggdraw() + draw_image(TPA.UK.Grapetree.3way.file)
TPA.UK.Grapetree.3way

#ggsave(paste0(Figure_output_directory, "SupFig4_TPA-PHE_Grapetree-3ways.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=145, height=180, device='pdf', dpi=1200)
And also do the HIV status plot
TPA.UK.Grapetree.HIV <- ggdraw() + draw_image(TPA.UK.Grapetree.HIV.file)
TPA.UK.Grapetree.HIV

#ggsave(paste0(Figure_output_directory, "SupFig5_TPA-PHE_Grapetree-HIV.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=185, height=110, device='pdf', dpi=1200)
### Phylogenetic context analyses
Ok, now lets look at some trees
First, let’s formalise BEAST tree plotting as three separate functions to enable other trees to be plotted the same way
full.beast2.tree <- read.beast(full.beast2.tree.file)
full.beast2.tree@phylo$tip.label <- gsub("\\|.+$","",full.beast2.tree@phylo$tip.label, perl=T)
################################################################################################
# function to extract a tree based on sublineage
Extract_sublineage_tree_for_plot <- function(my.beast.tree, my.metadata, my.phe.meta, my.sublineage){
# get all tips to include from metadata, then calculate MRCA from tree
sublineage.test.mrca <- getMRCA(my.beast.tree@phylo, as.character(unlist(my.metadata[my.metadata$TPA.pinecone.sublineage==my.sublineage,"Sample_Name"])))
######
TPA.beast.subtree.test <- tree_subset(my.beast.tree, node=sublineage.test.mrca, levels_back=0)
return(TPA.beast.subtree.test)
}
#Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 1)
################################################################################################
################################################################################################
# Function to prepare a beast tree with timescale indicators, posterior support and 95% HPD bars
plot_beast_subtree_with_HPD <- function(my.beast.tree, my.metadata, my.phe.meta, mrsd.fulltree){
# get MRCD for tree
mrsd.Beast.tree.test.s <- max(as.numeric(unlist(my.metadata[my.metadata$Sample_Name %in% my.beast.tree@phylo$tip.label,"Sample_Year"])))
mrsd.Beast.tree.test <- lubridate::ymd(paste0(mrsd.Beast.tree.test.s,"-06-01"))
mrsd.Beast.tree.fulltree <- lubridate::ymd(mrsd.fulltree)
#mrsd.Beast.tree.test
# plot basic tree
options(ignore.negative.edge=TRUE)
p.TPA.beast.subtree.test <- ggtree(my.beast.tree, mrsd=mrsd.Beast.tree.test, ladderize = T, size=0.4) + scale_x_continuous(breaks=seq(1960,2020,10), minor_breaks=seq(2000, 2020, 1)) +
theme_tree2() +
# Add date lines for easy interpretation
theme(panel.grid.major = element_line(color="grey50", size=.2),
panel.grid.minor = element_line(color="grey85", size=.2),
panel.grid.major.y = element_blank(),
panel.grid.minor.y = element_blank())
# Add posterior support as node points
p.TPA.beast.subtree.test <- p.TPA.beast.subtree.test + geom_point2(aes(subset=(!isTip & as.numeric(posterior)>0.8)),color="gray60",size=2,alpha=0.5, shape=18) +
geom_point2(aes(subset=(!isTip & as.numeric(posterior)>0.91)),color="gray40",size=3,shape=18,alpha=0.5) +
geom_point2(aes(subset=(!isTip & as.numeric(posterior)>=0.96)),color="black",size=3,shape=18,alpha=0.5)
######
# extract 95% HPD intervals - geom_range seems unable to do correctly with this tree (known bug for tip dated trees), so extract data and plot using geom_segment
TPA.beast.subtree.test.data <- fortify(my.beast.tree)
minmax <- t(matrix(unlist(TPA.beast.subtree.test.data[!is.na(TPA.beast.subtree.test.data$height_0.95_HPD),"height_0.95_HPD"]),nrow=2))
bar_df <- data.frame(node_id=TPA.beast.subtree.test.data[!is.na(TPA.beast.subtree.test.data$height_0.95_HPD),"node"],as.data.frame(minmax))
names(bar_df) <- c('node_id','min','max')
bar_df <- bar_df %>% filter(node_id > Ntip(my.beast.tree@phylo))
bar_df <- bar_df %>% left_join(TPA.beast.subtree.test.data, by=c('node_id'='node')) #%>% select(node_id,min,max,y)
#mrcd.decimal <- decimal_date(mrsd.Beast.tree.test)
mrcd.decimal <- decimal_date(mrsd.Beast.tree.fulltree)
# Now add HPDs to plot
p.TPA.beast.subtree.test <- p.TPA.beast.subtree.test + geom_segment(aes(x=mrcd.decimal-max, y=y, xend=mrcd.decimal-min, yend=y), data=bar_df, color='red', alpha=0.2, size=2.0)
# Output tree
return(p.TPA.beast.subtree.test)
}
################################################################################################
################################################################################################
# Function to add metadata to tree
# Has two optional arguments "initial.track.offset" and "track.scaling" which can be used to alter the width and positioning of metadata tracks
plot_beast_subtree_with_PHE_metadata <- function(my.beast.tree.input, my.metadata, my.phe.meta, initial.track.offset, track.scaling){
# Add code to allow scaling up of the track offsets and widths - useful for much bigger length trees
if(missing(initial.track.offset)){
initial.track.offset <- 0
}
if(missing(track.scaling)){
track.scaling <- 1
}
# Calculate amount to offset each heatmap track
offset.dist <- 4*track.scaling
track.width <- (1/max(my.beast.tree.input$data$height)*3)*track.scaling
# make a list of taxa used in this plot
my.taxa.list <- as.character(unlist(filter(my.beast.tree.input$data, isTip==TRUE) %>% select(label)))
# make a color scale for sampling years
#PHE.sublintest.year.cols <- data.frame(year=sort(unique(as.numeric(unlist(my.metadata[(my.metadata$Sample_Name %in% my.taxa.list),"Sample_Year"],use.names=F)))),stringsAsFactors = T)
#PHE.sublintest.year.cols$year.cols <- colorRampPalette(brewer.pal(7, "YlOrRd"))(nrow(PHE.sublintest.year.cols))
# Or alternatively, use a common colour scheme for all data (maybe more sensible)
PHE.sublintest.year.cols <- data.frame(year=TPA.year.cuttoff.cols$date.cuttoff, year.cols=TPA.year.cuttoff.cols$date.cuttoff.col, stringsAsFactors = F)
# make metadata file for UK regions present in sublineage
sublin.test.region.meta <- data.frame(row.names=as.character(unlist(my.phe.meta[my.phe.meta$Sample_Name %in% my.taxa.list,"Sample_Name"])), Region=as.character(unlist(my.phe.meta[my.phe.meta$Sample_Name %in% my.taxa.list,"phe_centre"])), stringsAsFactors = F)
# Add heatmap strips
# Sample Year
#TPA.beast.subtree.test.global.plot1.regional <- gheatmap(my.beast.tree.input, TPA.rawseq.all.Years.p, color=NULL,width=track.width, offset=initial.track.offset+offset.dist,colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
#scale_fill_manual(name="Year", values=PHE.sublintest.year.cols$year.cols,breaks=PHE.sublintest.year.cols$year, guide = guide_legend(order = 1, ncol=2)) +
#ggnewscale::new_scale_fill()
TPA.beast.subtree.test.global.plot1.regional <- gheatmap(my.beast.tree.input, TPA.rawseq.year.cuttoff.p, color=NULL,width=track.width, offset=initial.track.offset+offset.dist,colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="Year", values=PHE.sublintest.year.cols$year.cols,breaks=PHE.sublintest.year.cols$year, guide = guide_legend(order = 1, ncol=2)) +
ggnewscale::new_scale_fill()
# Add country
TPA.beast.subtree.test.global.plot1.regional <- gheatmap(TPA.beast.subtree.test.global.plot1.regional, TPA.rawseq.countries.p, color=NULL,width=track.width, offset=initial.track.offset+(offset.dist*2),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="Country", values=continental.country.cols.brew2$country.col, breaks=continental.country.cols.brew2$Geo_Country, guide = guide_legend(order = 2)) +
ggnewscale::new_scale_fill()
# UK or non-UK
TPA.beast.subtree.test.global.plot1.regional <- gheatmap(TPA.beast.subtree.test.global.plot1.regional,
TPA.rawseq.UK.p, color=NULL,width=track.width,offset=initial.track.offset+(offset.dist*3), colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) +
scale_fill_manual(name="England/Other", breaks=c("England","Other"), values=c("black","grey95"), na.value = "white", guide = guide_legend(order = 3, ncol=2)) +
ggnewscale::new_scale_fill()
# UK PHE region
TPA.beast.subtree.test.global.plot1.regional <- gheatmap(TPA.beast.subtree.test.global.plot1.regional, sublin.test.region.meta, color=NULL,width=track.width, offset=initial.track.offset+(offset.dist*4),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="UKHSA Region", values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region, na.value = "white", guide = guide_legend(order = 4)) +
ggnewscale::new_scale_fill()
# TPA sublineage
#TPA.beast.subtree.test.global.plot1.regional <- gheatmap(TPA.beast.subtree.test.global.plot1.regional, data.frame(row.names=TPA.meta2.1$Sample_Name, Sublineage=TPA.meta2.1$TPA.pinecone.sublineage, stringsAsFactors = F), color=NULL,width=track.width,offset=initial.track.offset+(offset.dist*5), colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=2.5) +
#scale_fill_manual(name="Sublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage, guide = guide_legend(order = 5))
TPA.beast.subtree.test.global.plot1.regional <- TPA.beast.subtree.test.global.plot1.regional + theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
new_scale_fill() +
geom_rootedge(2) +
NULL
# calculate number of taxa
test.taxacount <- length(my.taxa.list)
# Adjust final plot x and y axis to make space for labels using taxa counts
x.axis.limits <- ggplot_build(TPA.beast.subtree.test.global.plot1.regional)$layout$panel_scales_x[[1]]$range$range
TPA.beast.subtree.test.global.plot1.regional <- TPA.beast.subtree.test.global.plot1.regional +
coord_cartesian(y=c(-0.5-(test.taxacount/15),test.taxacount+2), x=c(x.axis.limits[1],x.axis.limits[2]+3))
return(TPA.beast.subtree.test.global.plot1.regional)
}
################################################################################################
Great, now let’s plot a full beast tree
# function for x-axis time breaks needs tweaking for the full tree
TPA.Global.full.BeastTree.ukmeta <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(my.beast.tree = full.beast2.tree, my.metadata = TPA.meta2.1, my.phe.meta = PHE.metadata.linked, mrsd.fulltree = "2019-06-01") + scale_x_continuous(breaks=seq(1400,2020,50), minor_breaks=seq(1950, 2020, 5)), my.metadata = TPA.meta2.1, my.phe.meta = PHE.metadata.linked, track.scaling = 5)
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing
scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
TPA.Global.full.BeastTree.ukmeta

#ggsave(paste0(Figure_output_directory,"SupFig7_TPA_FullBeastTree.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=185, height=240, device='pdf', dpi=1200)
Now do sublineage plots
Make some plots
# Sublineage 1
sublineage.1.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 1), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, track.scaling = 1.2)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# Sublineage.2
sublineage.2.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 2), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, track.scaling = 1)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# Sublineage.8
sublineage.8.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 8), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, track.scaling = 1.1)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# Sublineage.14
sublineage.14.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 14), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, track.scaling = 1.1)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Plot together?
Maybe with sublineage 1 expanded?
p.beast.trees.heatmap.sublineages.combi.offset1 <- plot_grid(sublineage.2.tree.heatmap,
sublineage.8.tree.heatmap,
sublineage.14.tree.heatmap,
ncol=2, labels=c("B - Sublineage 2","C - Sublineage 8","D - Sublineage 14"), label_size=panel.lab.size, scale=0.95, vjust=1.0)
p.beast.trees.heatmap.sublineages.combi.offset2 <- plot_grid(sublineage.1.tree.heatmap, p.beast.trees.heatmap.sublineages.combi.offset1, labels=c("A - Sublineage 1", ""), label_size=panel.lab.size, scale=0.975, ncol=2, rel_widths=c(6,11), vjust=2.5)
p.beast.trees.heatmap.sublineages.combi.offset2

#ggsave(paste0(Figure_output_directory,"SupFig8_TPA-PHE_Sublineage-BeastTrees.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=265, height=230, device='pdf', dpi=1200)
Need to explore sublineage 14 a bit more to get dates for those subclades
sublineage.14.tree.heatmap + geom_tiplab(size=theme.text.size.within, linesize=0.4) #3

# Ok, there are multiple subclades in this tree
sublineage.14.tree.heatmap.data <- sublineage.14.tree.heatmap$data
# getMRCA(full.beast2.tree@phylo,c("PHE150150A","NL14","TPA_BCC122","TPA_BCC126","PHE140076A","TPA_UKBRG008")) 982
# full.beast2.tree@phylo$tip.label[phangorn::Descendants(full.beast2.tree@phylo, 982, type = c("tips"))[[1]]]
sublineage.14.lowerclade.list <- c("NL17", "NL19", "PHE140085A", "PHE140089A", "PHE150118A", "PHE150121A", "PHE150133A", "PHE150143A", "PHE150145A", "PHE150162A", "PHE150166A", "PHE150168A", "PHE160224A", "PHE160243A", "PHE160255A", "PHE160276A", "PHE160290A", "PHE160302A", "PHE160306A", "PHE170333A", "PHE170349A", "PHE170374A", "PHE170381A", "PHE170664A", "TPA_ESBCN005", "TPA_UKBIR032")
sublineage.14.upperclade.list <- c("NL14", "PHE140076A", "PHE150149A", "PHE150150A", "PHE150170A", "PHE160196A", "PHE160263A", "PHE160274A", "PHE160287A", "PHE160294A", "PHE160316A", "PHE160317A", "PHE170372A", "PHE170386A", "PHE170397A", "PHE170405A", "TPA_BCC081", "TPA_BCC088", "TPA_BCC089", "TPA_BCC101", "TPA_BCC122", "TPA_BCC126", "TPA_BCC136", "TPA_BCC169", "TPA_HUN180004", "TPA_HUN190020", "TPA_UKBIR044", "TPA_UKBRG007", "TPA_UKBRG008")
# Get MRCA date for lower clade
sublineage.14.lowerclade.list.tmrca <- sublineage.14.tree.heatmap.data[sublineage.14.tree.heatmap.data$node==getMRCA(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 14)@phylo, sublineage.14.lowerclade.list),"x"]
paste0("TMRCA for sublineage 14 lower clade: ",sublineage.14.lowerclade.list.tmrca)
[1] "TMRCA for sublineage 14 lower clade: 2006.53850498154"
# Get MRCA date for upper clade
sublineage.14.upperclade.list.tmrca <- sublineage.14.tree.heatmap.data[sublineage.14.tree.heatmap.data$node==getMRCA(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 14)@phylo, sublineage.14.upperclade.list),"x"]
paste0("TMRCA for sublineage 14 upper clade: ",sublineage.14.upperclade.list.tmrca)
[1] "TMRCA for sublineage 14 upper clade: 1999.15025243934"
Extract key information for sublineage 6 (two samples)
sublineage.6.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 6), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
sublineage.6.tree.heatmap.data <- sublineage.6.tree.heatmap$data
# Get MRCA date for upper clade
sublineage.6.beasttree.tmrca <- as.numeric(sublineage.6.tree.heatmap.data[sublineage.6.tree.heatmap.data$node==getMRCA(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 6)@phylo, c("PHE130048A", "PHE160283A")),"branch"])
paste0("TMRCA for sublineage 6 upper clade: ",sublineage.6.beasttree.tmrca)
[1] "TMRCA for sublineage 6 upper clade: 1982.61865062176"
### Extract sample & population statistics from datasets for use in manuscript text
Dataset and Geographical distributions
# dataset counts
paste0("Total UK samples in cleaned/deduplicated dataset: ",nrow(PHE.metadata.linked))
[1] "Total UK samples in cleaned/deduplicated dataset: 237"
paste0("Of which: ",nrow(PHE.metadata.linked[PHE.metadata.linked$is.PHE=="PHE",])," from PHE Ref lab at Colindale")
[1] "Of which: 195 from PHE Ref lab at Colindale"
paste0("Of which: ",nrow(PHE.metadata.linked[PHE.metadata.linked$is.PHE=="Other",])," from other labs")
[1] "Of which: 42 from other labs"
# proportion with geographical data
paste0("From UK samples, ", nrow(PHE.metadata.linked[(PHE.metadata.linked$phe_centre %notin% c("Not Known","UK (not England)")),])," were grouped into one of the 9 PH regions")
[1] "From UK samples, 217 were grouped into one of the 9 PH regions"
paste0("From UK samples, ", nrow(PHE.metadata.linked[PHE.metadata.linked$phe_centre=="UK (not England)",]), " were referred from outside England")
[1] "From UK samples, 2 were referred from outside England"
paste0("From UK samples, ", nrow(PHE.metadata.linked[PHE.metadata.linked$phe_centre=="Not Known",]), " had unknown region")
[1] "From UK samples, 18 had unknown region"
# counts & fractions by PHE region
PHE.geo.count
NA
Gender Orientation stats
PHE.orientation.counts
PHE.geo.orientation.counts
PHE.geo.HIV.counts
PHE.sublineage.orientation.counts
PHE.sublineage.Age
Sublineage Distributions
PHE.Lineage.count
PHE.sublin.count
PHE.geo.sublineage
Macrolide resistance stats
UK.macrolide.res <- PHE.metadata.linked %>%
dplyr::group_by(A2058G, A2059G) %>%
dplyr::summarise(Count.allele=n()) %>%
dplyr::ungroup() %>%
dplyr::mutate(total.count=sum(Count.allele), perc.allele=round((Count.allele/total.count)*100,1))
`summarise()` has grouped output by 'A2058G'. You can override using the `.groups` argument.
UK.macrolide.res
UK.macrolide.res.sublin <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage, A2058G, A2059G) %>%
dplyr::summarise(Count.allele=n()) %>%
dplyr::ungroup() %>%
dplyr::group_by(TPA.pinecone.sublineage) %>%
dplyr::mutate(total.count=sum(Count.allele), perc.allele=round((Count.allele/total.count)*100,1))
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'A2058G'. You can override using the `.groups` argument.
UK.macrolide.res.sublin
# Calculate long form df, with different 23S alleles (A2058G, A2059G, WT, Uncertain) v.s. sublineage
UK.macrolide.res.sublin.long <- PHE.metadata.linked %>%
mutate(Resistance.allele=ifelse(A2058G=="Yes", "A2058G", ifelse(A2059G=="Yes", "A2059G", ifelse((A2058G=="No" & A2059G=="No"),"Wild Type", "Uncertain")))) %>%
dplyr::group_by(TPA.pinecone.sublineage, Resistance.allele) %>%
dplyr::summarise(Count.per.sublin.Macrolides=n()) %>%
dplyr::mutate(total.sublin=sum(Count.per.sublin.Macrolides),
fraction=Count.per.sublin.Macrolides/total.sublin) %>%
#dplyr::ungroup() %>%
dplyr::arrange((Resistance.allele), .by_group=T) %>%
dplyr::mutate(cum_fract = cumsum(fraction)) %>%
dplyr::mutate(cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(Resistance.allele = factor(Resistance.allele, levels=rev(c("A2058G", "A2059G", "Uncertain", "Wild Type"))))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Make plot of macrolide resistance by sublineages
p.sublin.Macrolides.hbarplot <- ggplot(UK.macrolide.res.sublin.long, aes(Count.per.sublin.Macrolides, y=TPA.pinecone.sublineage, fill=Resistance.allele)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
scale_fill_manual(name="Macrolide\nResistance\nAllele",values=c("indianred2", "steelblue1","grey55", "grey90"), breaks=c("A2058G", "A2059G", "Uncertain", "Wild Type")) +
labs(y="TPA Sublineage", x="Proportion with Macrolide Resistance Allele") +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
guides(fill=guide_legend(ncol=2)) +
geom_text(data=UK.macrolide.res.sublin.long, aes(cum_fract.mid, y=TPA.pinecone.sublineage,label=Count.per.sublin.Macrolides), size=theme.text.size.within, inherit.aes = F) +
NULL
p.sublin.Macrolides.hbarplot

# Combine plot with sublineage count bars
p.sublin.Macrolides.hbarplot.combi <- plot_grid(p.sublineage.hbarplot + guides(fill=guide_legend(ncol=3)), p.sublin.Macrolides.hbarplot + y.theme.strip, nrow=1, align=T, labels=c("A", "B"), label_size=panel.lab.size)
p.sublin.Macrolides.hbarplot.combi

#ggsave(paste0(Figure_output_directory,"SupFig9_TPA-PHE_Sublin-Macrolide-Res.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=160, height=120, device='pdf', dpi=1200)
Pairwise SNP analysis
OK, want to investigate the different patterns observable for the North East of England (pale blue) in Sublineage 1
Multiple ways we can do this - including SNP distances (also multiple ways to do that)
###
#Use phylogenetic distance from the SNP scaled tree
TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist <- ape::cophenetic.phylo(TPA.pyjar.tree.subset.uk)
TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt <- data.frame(Taxa1=row.names(TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist), TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist, stringsAsFactors = F) %>% tidyr::gather(Taxa2, Distance.Phylo, -Taxa1)
# Taxa Comparisons label
TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt$Taxa_combination <- sapply(1:nrow(TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt), function (x) paste0(sort(c(as.character(TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt$Taxa1[x]),as.character(TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt$Taxa2[x]))),collapse="___"))
# Merge together
#TPA.WGS.alignment.data.dist.melt <- dplyr::left_join(TPA.WGS.alignment.data.dist.melt, TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt[,c("Taxa_combination","Distance.Phylo")], by="Taxa_combination")
TPA.WGS.alignment.data.dist.melt <- TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt
TPA.WGS.alignment.data.dist.melt <- unique(TPA.WGS.alignment.data.dist.melt)
Ok, now bring in some metadata and comparisons
# Bring in and merge metadata
PHE.meta.pairwise.t1 <- PHE.metadata.linked[,c("Sample_Name","year","phe_centre","london","gender_orientation","hivpos","age_group","ukborn","TPA.pinecone.sublineage", "TPA_Lineage","Geo_Country","is.UK","is.PHE", "Sample_Year","date.decimal")]
colnames(PHE.meta.pairwise.t1) <- paste0(colnames(PHE.meta.pairwise.t1),".t1")
colnames(PHE.meta.pairwise.t1)[1] <- "Taxa1"
PHE.meta.pairwise.t2 <- PHE.metadata.linked[,c("Sample_Name","year","phe_centre","london","gender_orientation","hivpos","age_group","ukborn","TPA.pinecone.sublineage", "TPA_Lineage","Geo_Country","is.UK","is.PHE", "Sample_Year","date.decimal")]
colnames(PHE.meta.pairwise.t2) <- paste0(colnames(PHE.meta.pairwise.t2),".t2")
colnames(PHE.meta.pairwise.t2)[1] <- "Taxa2"
PHE.alignment.data.dist.melt.meta <- plyr::join(TPA.WGS.alignment.data.dist.melt,PHE.meta.pairwise.t1, by="Taxa1", type="left")
PHE.alignment.data.dist.melt.meta <- plyr::join(PHE.alignment.data.dist.melt.meta,PHE.meta.pairwise.t2, by="Taxa2", type="left")
# Exclude missing data (e.g. missing sublineage) - this will also remove non-UK samples, since full metadata is missing here
PHE.alignment.data.dist.melt.meta <- PHE.alignment.data.dist.melt.meta[!is.na(PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t1),]
PHE.alignment.data.dist.melt.meta <- PHE.alignment.data.dist.melt.meta[!is.na(PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t2),]
Define comparisons
# Same sample
PHE.alignment.data.dist.melt.meta$same.sample <- ifelse(PHE.alignment.data.dist.melt.meta$Taxa1==PHE.alignment.data.dist.melt.meta$Taxa2,"same", "different")
# Years between samples
PHE.alignment.data.dist.melt.meta$year.distance <- abs(as.numeric(PHE.alignment.data.dist.melt.meta$year.t1) - as.numeric(PHE.alignment.data.dist.melt.meta$year.t2))
PHE.alignment.data.dist.melt.meta$Sample_Year.distance <- abs(as.numeric(PHE.alignment.data.dist.melt.meta$Sample_Year.t1) - as.numeric(PHE.alignment.data.dist.melt.meta$Sample_Year.t2))
# Years between decimal date (more precise temporal distance)
PHE.alignment.data.dist.melt.meta$decimal.date.distance <- abs(as.numeric(PHE.alignment.data.dist.melt.meta$date.decimal.t1) - as.numeric(PHE.alignment.data.dist.melt.meta$date.decimal.t2))
# Epidemiological time between - catagorical
PHE.alignment.data.dist.melt.meta$epi.time.distance.cat <- ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<1/12,"month", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=3/12, "quarter", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=6/12, "half year", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=1, "1 year",ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=2, "2 years", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=3, "3 years", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=4, "4 years", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=5, "5 years", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=6, "6 years",">6 years")))))))))
PHE.alignment.data.dist.melt.meta$epi.time.distance.cat <- factor(PHE.alignment.data.dist.melt.meta$epi.time.distance.cat, levels=c("month", "quarter","half year","1 year", "2 years", "3 years", "4 years", "5 years", "6 years", ">6 years"))
PHE.alignment.data.dist.melt.meta$epi.time.distance.cat.years <- ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=1, "0", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=2, "1", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=3, "2", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=4, "3", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=5, "4", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=6, "5",">5"))))))
# Same country
PHE.alignment.data.dist.melt.meta$same.country <- ifelse(PHE.alignment.data.dist.melt.meta$Geo_Country.t1 == PHE.alignment.data.dist.melt.meta$Geo_Country.t2, "same", "different")
# Is UK
PHE.alignment.data.dist.melt.meta$both.uk <- ifelse(PHE.alignment.data.dist.melt.meta$is.UK.t1 == PHE.alignment.data.dist.melt.meta$is.UK.t2, "same", "different")
# Is PHE
PHE.alignment.data.dist.melt.meta$both.PHE <- ifelse(PHE.alignment.data.dist.melt.meta$is.PHE.t1 == PHE.alignment.data.dist.melt.meta$is.PHE.t2, "same", "different")
# Same TPA Lineage (cleaned up classifications)
PHE.alignment.data.dist.melt.meta$same.TPA.Lineage <- ifelse(PHE.alignment.data.dist.melt.meta$TPA_Lineage.t1==PHE.alignment.data.dist.melt.meta$TPA_Lineage.t2, "same", "different")
PHE.alignment.data.dist.melt.meta$same.TPA.Lineage <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function(x) ifelse((PHE.alignment.data.dist.melt.meta$TPA_Lineage.t1[x]=="0" | PHE.alignment.data.dist.melt.meta$TPA_Lineage.t2[x]=="0"),NA,PHE.alignment.data.dist.melt.meta$same.TPA.Lineage[x]))
# Same TPA sublineage
PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster <- ifelse(PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t1==PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t2,"same", "different")
PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function(x) ifelse(((PHE.alignment.data.dist.melt.meta$same.sample[x]=="different" & PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t1[x]=="Singleton") |(PHE.alignment.data.dist.melt.meta$same.sample[x]=="different" & PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t2[x]=="Singleton")),"different",PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster[x]))
# Define Genetic relationships hierarchically
PHE.alignment.data.dist.melt.meta$genomic.cluster.hierarchy <- ifelse(PHE.alignment.data.dist.melt.meta$Distance==0,"Zero_SNPs", ifelse(PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster=="same","Same Sublineage", ifelse(PHE.alignment.data.dist.melt.meta$same.TPA.Lineage=="same", "Same Lineage","Different Lineage")))
PHE.alignment.data.dist.melt.meta$genomic.cluster.hierarchy.ph <- ifelse(PHE.alignment.data.dist.melt.meta$Distance.Phylo==0,"Zero_SNPs", ifelse(PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster=="same","Same Sublineage", ifelse(PHE.alignment.data.dist.melt.meta$same.TPA.Lineage=="same", "Same Lineage","Different Lineage")))
# Same PHE region
PHE.alignment.data.dist.melt.meta$same.PHE.region <- ifelse(PHE.alignment.data.dist.melt.meta$phe_centre.t1==PHE.alignment.data.dist.melt.meta$phe_centre.t2, "same", "different")
PHE.alignment.data.dist.melt.meta$PHE.centre.combination <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function (x) paste0(sort(c(as.character(PHE.alignment.data.dist.melt.meta$phe_centre.t1[x]),as.character(PHE.alignment.data.dist.melt.meta$phe_centre.t2[x]))),collapse="___"))
# does the combination included London?
PHE.alignment.data.dist.melt.meta$involves.London <- ifelse(PHE.alignment.data.dist.melt.meta$phe_centre.t1=="London" | PHE.alignment.data.dist.melt.meta$phe_centre.t2=="London", "London", "not-London")
# Orientation pair
PHE.alignment.data.dist.melt.meta$Orientation_combination <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function (x) paste0(sort(c(as.character(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]),as.character(PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]))),collapse="___"))
#PHE.alignment.data.dist.melt.meta$Orientation.Class <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function (x) ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="MSM" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="MSM", "MSM",
# ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="MSM" | PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="MSM", "Mixed",
# ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="MSW" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="WSM","Heterosexual",
# ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="WSM" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="MSW","Heterosexual","Unknown")))))
PHE.alignment.data.dist.melt.meta$Orientation.Class <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function (x) ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="GBMSM" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="GBMSM", "GBMSM",
ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x] %in% c("MSW","WSM") & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x] %in% c("MSW","WSM"),"Heterosexual",
ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="GBMSM" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x] %in% c("MSW","WSM"), "Mixed",
ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x] %in% c("MSW","WSM") & PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="GBMSM", "Mixed", "Unknown")))))
# Country Comparisons label
PHE.alignment.data.dist.melt.meta$Country_combinations <- paste0(PHE.alignment.data.dist.melt.meta$Geo_Country.t1,"___",PHE.alignment.data.dist.melt.meta$Geo_Country.t2)
# Subset to PHE data only (effectively already done, but let's be explicit)
PHE.TPA.alignment.data.dist.melt.meta <- PHE.alignment.data.dist.melt.meta[(PHE.alignment.data.dist.melt.meta$both.uk=="same" & PHE.alignment.data.dist.melt.meta$both.PHE=="same"),]
PHE.TPA.alignment.data.dist.melt.meta <- PHE.TPA.alignment.data.dist.melt.meta[PHE.TPA.alignment.data.dist.melt.meta$PHE.only=="PHE",]
PHE.TPA.alignment.data.dist.melt.meta <- PHE.alignment.data.dist.melt.meta[(PHE.alignment.data.dist.melt.meta$both.uk=="same"),]
# Make single sided
PHE.TPA.alignment.data.dist.melt.meta <- PHE.TPA.alignment.data.dist.melt.meta[!duplicated(PHE.TPA.alignment.data.dist.melt.meta$Taxa_combination),]
### Perform a more detailed analysis of samples from the North East of England
Do a more detailed exploration of the North East of England
PHE.metadata.linked2.region_NorthEast <- PHE.metadata.linked[PHE.metadata.linked$phe_centre=="North East",]
# Constrain by samples being from the North East
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta[(PHE.alignment.data.dist.melt.meta$phe_centre.t1=="North East" & PHE.alignment.data.dist.melt.meta$same.sample=="different"),]
# Constrain by the same PHE region
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[PHE.alignment.data.dist.melt.meta.NorthEast.clusters$same.PHE.region=="same",]
#Just plot these distros
p.NorthEast.Pairwise.SNPs.unconstrained <- ggplot(PHE.alignment.data.dist.melt.meta.NorthEast.clusters, aes(Distance.Phylo)) +
geom_histogram(binwidth = 1) +
theme_bw() +
theme.text.size +
labs(x="Pairwise SNP Distance", y="Comparison Count")
p.NorthEast.Pairwise.SNPs.unconstrained

Make a single linkage network from the North East samples
# Constrain by SNP distance (looser than previously - we just want to find basic groupings within sublineage 1 for NE samples)
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[PHE.alignment.data.dist.melt.meta.NorthEast.clusters$Distance.Phylo<=2,]
# And make sure that we actually have genetic distance data for all samples within the network
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[!is.na(PHE.alignment.data.dist.melt.meta.NorthEast.clusters$Distance.Phylo),]
# cleanup some data noise
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[!is.na(PHE.alignment.data.dist.melt.meta.NorthEast.clusters$year.t1),]
# prepare intput data (with edge info)
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1 <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[,c("Taxa1","Taxa2","Distance.Phylo","decimal.date.distance","year.distance","Orientation.Class","epi.time.distance.cat")]
############
# some issues with update to R4 - double sided matrix
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$edgename <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1), function(x) paste0(sort(as.character(unlist(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1[x,c("Taxa1","Taxa2")]))),collapse="___"))
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1 <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1[!duplicated(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$edgename),]
# Also having an issue with taxa as factors here
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Taxa1 <- as.character(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Taxa1)
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Taxa2 <- as.character(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Taxa2)
############
#inverse weight
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Distance.inv <- 1/PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Distance.Phylo
# Make actual network
set.seed(1235)
PHE.NorthEast.network <- network(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1, matrix.type = "edgelist", ignore.eval = FALSE, directed = F)
PHE.NorthEast.network.gg <- ggnetwork(PHE.NorthEast.network, layout = "kamadakawai", weights = "Distance.inv")
PHE.NorthEast.network.gg$Taxa1 <- PHE.NorthEast.network.gg$vertex.names
# extract temporal clusters from network
PHE.NorthEast.network.ig <- asIgraph(PHE.NorthEast.network)
PHE.NorthEast.network.components <- data.frame(Taxa1=network.vertex.names(PHE.NorthEast.network), vertex.no=as.vector(V(PHE.NorthEast.network.ig)), cluster=igraph::components(PHE.NorthEast.network.ig)$membership)
# For ease of story telling in the paper, flip clusters 2 and 3 around (so we can talk about 2 first)
PHE.NorthEast.network.components <- PHE.NorthEast.network.components %>%
dplyr::mutate(cluster.old=cluster, cluster=ifelse(cluster.old==2, 3, ifelse(cluster.old==3,2,cluster.old)))
PHE.NorthEast.network.components$Cluster <- paste0("Cluster",PHE.NorthEast.network.components$cluster)
# merge metadata back in
PHE.NorthEast.network.gg <- plyr::join(PHE.NorthEast.network.gg, data.frame(Taxa1=PHE.metadata.linked$Sample_Name, PHE.metadata.linked[,c("phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], stringsAsFactors = F),by="Taxa1", type="left")
PHE.NorthEast.network.gg <- plyr::join(PHE.NorthEast.network.gg, data.frame(Taxa1=PHE.NorthEast.network.components$Taxa1, Cluster=PHE.NorthEast.network.components$Cluster), by="Taxa1", type="left")
Plot network
# Plot network
p.PHE.NorthEast.network.2SNP <- ggplot(PHE.NorthEast.network.gg, aes(x = x, y = y, xend = xend, yend = yend)) +
geom_edges(alpha=0.90, curvature = 0.2, aes(color=factor(Distance.Phylo), linetype=factor(Distance.Phylo))) +
scale_color_manual(values=c("grey5","grey55","grey85"), name="SNP\nDistance") +
scale_linetype(name="SNP\nDistance") +
theme_blank() +
ggnewscale::new_scale_color() + ggnewscale::new_scale("size") +
geom_nodelabel(aes(color=gender_orientation, label=paste(Taxa1,year,sep="\n"),fontface = "bold"), alpha=0.8, size=theme.text.size.within-0.4, label.size=0.15, label.padding = unit(0.05, "lines")) +
geom_nodes(size=1.0, aes(color=gender_orientation)) +
scale_color_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
NULL
p.PHE.NorthEast.network.2SNP

Ok, so three networks. Clear differentiation of a heterosexual network (with 0-snp distances) and two predominantly MSM networks
Let’s look at the phylogenetic context of those North East clusters we’ve defined. Pull out subtrees (from sublineage 1 subtree)
# Cluster 1
Beast.tree.NE.cluster1 <- getMRCA(full.beast2.tree@phylo, PHE.NorthEast.network.components[PHE.NorthEast.network.components$Cluster=="Cluster1","Taxa1"])
Beast.tree.NE.cluster1.subtree <- tree_subset(full.beast2.tree, node=Beast.tree.NE.cluster1, levels_back=0)
p.Beast.tree.NE.cluster1.subtree <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Beast.tree.NE.cluster1.subtree, TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, initial.track.offset = 10)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# Can't fit in tip labs, but since this is a polyphyletic subtree, it would be helpful to add a track to highlight the NE strains
PHE.metadata.linked$is.NorthEast <- ifelse(PHE.metadata.linked$phe_centre=="North East","North East", "Other England")
p.Beast.tree.NE.cluster1.subtree.cluster.highlight <- gheatmap(p.Beast.tree.NE.cluster1.subtree, data.frame(row.names=PHE.metadata.linked$Sample_Name, `North East`=PHE.metadata.linked$is.NorthEast), color=NULL,width=(1/max(p.Beast.tree.NE.cluster1.subtree$data$height)*3), offset=10+(4*5),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="North East\nEngland", values=c("#A6CEE3","grey95"), breaks=c("North East","Other England"), na.value = "white", guide = guide_legend(order = 5)) +
ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# Just confirm the ClusterIDs for this subtree (make sure it doesn't enclose other clusters)
p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID <- gheatmap(p.Beast.tree.NE.cluster1.subtree.cluster.highlight, data.frame(row.names=PHE.NorthEast.network.components$Taxa1, ClusterID=PHE.NorthEast.network.components$Cluster), color=NULL,width=(1/max(p.Beast.tree.NE.cluster1.subtree$data$height)*3), offset=10+(4*6),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="North East\nCluster", values=c("#7fc97f","#beaed4","#fdc086"), breaks=c("Cluster1","Cluster2","Cluster3"), na.value = "white", guide = guide_legend(order = 6)) +
ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# add a bit more room to the x axis
p.Beast.tree.NE.cluster1.subtree.cluster.highlight.x.axis.limits <- ggplot_build(p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID)$layout$panel_scales_x[[1]]$range$range
p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID <- p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID +
coord_cartesian(x=c(p.Beast.tree.NE.cluster1.subtree.cluster.highlight.x.axis.limits[1],p.Beast.tree.NE.cluster1.subtree.cluster.highlight.x.axis.limits[2]+4), y=c(-0.5-(length(unique(p.Beast.tree.NE.cluster1.subtree.cluster.highlight$data$label))/15),length(unique(p.Beast.tree.NE.cluster1.subtree.cluster.highlight$data$label))+2)) +
theme(legend.margin = margin(-0.5,0,0,0, unit="mm"))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
#p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID
#######################
# Cluster 2
Beast.tree.NE.cluster2 <- getMRCA(full.beast2.tree@phylo, PHE.NorthEast.network.components[PHE.NorthEast.network.components$Cluster=="Cluster2","Taxa1"])
Beast.tree.NE.cluster2.subtree <- tree_subset(full.beast2.tree, node=Beast.tree.NE.cluster2, levels_back=1)
p.Beast.tree.NE.cluster2.subtree <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Beast.tree.NE.cluster2.subtree, TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, initial.track.offset = 20) + geom_tiplab(size=theme.text.size.within, align=T, offset=5, linesize=0.4)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# Just add ClusterIDs for this subtree to highlight
p.Beast.tree.NE.cluster2.subtree <- gheatmap(p.Beast.tree.NE.cluster2.subtree, data.frame(row.names=PHE.NorthEast.network.components$Taxa1, ClusterID=PHE.NorthEast.network.components$Cluster), color=NULL,width=(1/max(p.Beast.tree.NE.cluster2.subtree$data$height)*3), offset=20+(4*5),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="North East\nCluster", values=c("#7fc97f","#beaed4","#fdc086"), breaks=c("Cluster1","Cluster2","Cluster3"), na.value = "white", guide = guide_legend(order = 5, ncol=2)) +
ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# add a bit more room to the x axis
p.Beast.tree.NE.cluster2.subtree.x.axis.limits <- ggplot_build(p.Beast.tree.NE.cluster2.subtree)$layout$panel_scales_x[[1]]$range$range
p.Beast.tree.NE.cluster2.subtree <- p.Beast.tree.NE.cluster2.subtree +
coord_cartesian(x=c(p.Beast.tree.NE.cluster2.subtree.x.axis.limits[1],p.Beast.tree.NE.cluster2.subtree.x.axis.limits[2]+12), y=c(-0.5-(length(unique(p.Beast.tree.NE.cluster2.subtree$data$label))/20)-1,length(unique(p.Beast.tree.NE.cluster2.subtree$data$label))+0.5)) +
theme(legend.margin = margin(-0.5,0,0,0, unit="mm"))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
#p.Beast.tree.NE.cluster2.subtree
############################
# Cluster 3
Beast.tree.NE.cluster3 <- getMRCA(full.beast2.tree@phylo, PHE.NorthEast.network.components[PHE.NorthEast.network.components$Cluster=="Cluster3","Taxa1"])
Beast.tree.NE.cluster3.subtree <- tree_subset(full.beast2.tree, node=Beast.tree.NE.cluster3, levels_back=1)
p.Beast.tree.NE.cluster3.subtree <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Beast.tree.NE.cluster3.subtree, TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, initial.track.offset = 26) + geom_tiplab(size=theme.text.size.within, align=T, offset=3, linesize=0.4)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# Just add ClusterIDs for this subtree to highlight
p.Beast.tree.NE.cluster3.subtree <- gheatmap(p.Beast.tree.NE.cluster3.subtree, data.frame(row.names=PHE.NorthEast.network.components$Taxa1, ClusterID=PHE.NorthEast.network.components$Cluster), color=NULL,width=(1/max(p.Beast.tree.NE.cluster3.subtree$data$height)*3), offset=26+(4*5),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="North East\nCluster", values=c("#7fc97f","#beaed4","#fdc086"), breaks=c("Cluster1","Cluster2","Cluster3"), na.value = "white", guide = guide_legend(order = 5, ncol=2)) +
ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# add a bit more room to the x axis
p.Beast.tree.NE.cluster3.subtree.x.axis.limits <- ggplot_build(p.Beast.tree.NE.cluster3.subtree)$layout$panel_scales_x[[1]]$range$range
p.Beast.tree.NE.cluster3.subtree <- p.Beast.tree.NE.cluster3.subtree +
coord_cartesian(x=c(p.Beast.tree.NE.cluster3.subtree.x.axis.limits[1],p.Beast.tree.NE.cluster3.subtree.x.axis.limits[2]+12), y=c(-0.5-(length(unique(p.Beast.tree.NE.cluster3.subtree$data$label))/20)-1,length(unique(p.Beast.tree.NE.cluster3.subtree$data$label))+0.5)) +
theme(legend.margin = margin(-0.5,0,0,0, unit="mm"))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
#p.Beast.tree.NE.cluster3.subtree
#p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID
#p.Beast.tree.NE.cluster2.subtree
#p.Beast.tree.NE.cluster3.subtree
Since Cluster 1 is really quite polyphyletic, it maybe more useful to show the clusters in context for that one
# Add North East identifier column
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight <- gheatmap(sublineage.1.tree.heatmap, data.frame(row.names=PHE.metadata.linked$Sample_Name, `North East`=PHE.metadata.linked$is.NorthEast), color=NULL,width=(1/max(sublineage.1.tree.heatmap$data$height)*3)*1.2, offset=0+(4*5)*1.2,colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="North East\nEngland", values=c("#A6CEE3","grey95"), breaks=c("North East","Other England"), na.value = "white", guide = guide_legend(order = 5)) +
ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# Just confirm the ClusterIDs for this subtree (make sure it doesn't enclose other clusters)
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight <- gheatmap(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight, data.frame(row.names=PHE.NorthEast.network.components$Taxa1, ClusterID=PHE.NorthEast.network.components$Cluster), color=NULL,width=(1/max(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight$data$height)*3)*1.2, offset=0+(4*6)*1.2,colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="North East\nCluster", values=c("#7fc97f","#beaed4","#fdc086"), breaks=c("Cluster1","Cluster2","Cluster3"), na.value = "white", guide = guide_legend(order = 6, ncol=2)) +
ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# add a bit more room to the x axis
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight.x.axis.limits <- ggplot_build(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight)$layout$panel_scales_x[[1]]$range$range
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight <- p.Beast.tree.sublineage1.NE.subtree.cluster.highlight +
coord_cartesian(x=c(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight.x.axis.limits[1],p.Beast.tree.sublineage1.NE.subtree.cluster.highlight.x.axis.limits[2]+4), y=c(-0.5-(length(unique(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight$data$label))/15),length(unique(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight$data$label))+2))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
# reduce spacing between legend scales
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight <- p.Beast.tree.sublineage1.NE.subtree.cluster.highlight + theme(legend.margin = margin(-0.95,0,0,0, unit="mm"))
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight

Plot together
p.Beast.tree.NE.subtrees.combi1 <- plot_grid(p.Beast.tree.NE.cluster2.subtree, p.Beast.tree.NE.cluster3.subtree, ncol=1, labels=c("C - Cluster 2", "D - Cluster 3"), vjust=1.0, label_size=panel.lab.size, scale=0.95)
p.Beast.tree.NE.subtrees.combi2 <- plot_grid(p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID, p.Beast.tree.NE.subtrees.combi1, ncol=2, rel_widths=c(3,2), labels=c("B - Cluster 1", ""), label_size=panel.lab.size)
p.Beast.tree.NE.subtrees.combi2

p.Beast.tree.NE.subtrees.combi3 <- plot_grid(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight, p.Beast.tree.NE.subtrees.combi1, ncol=2, rel_widths=c(8,7), labels=c("B - Sublineage 1 (All)", ""), label_size=panel.lab.size, scale=0.95, vjust=1.0)
p.Beast.tree.NE.subtrees.combi3

Look more closely at population demographics of these clusters
# Metadata on NE cluster 2
PHE.metadata.linked %>%
dplyr::filter(Sample_Name %in% Beast.tree.NE.cluster2.subtree@phylo$tip.label) %>%
dplyr::group_by(Geo_Country, is.NorthEast, gender_orientation) %>%
dplyr::summarise(Count=n())
`summarise()` has grouped output by 'Geo_Country', 'is.NorthEast'. You can override using the `.groups` argument.
# Metadata on NE cluster 3
PHE.metadata.linked %>%
dplyr::filter(Sample_Name %in% Beast.tree.NE.cluster3.subtree@phylo$tip.label) %>%
dplyr::group_by(Geo_Country, is.NorthEast, gender_orientation) %>%
dplyr::summarise(Count=n())
`summarise()` has grouped output by 'Geo_Country', 'is.NorthEast'. You can override using the `.groups` argument.
# Country info on NE cluster 3
TPA.meta2.1 %>%
dplyr::filter(Sample_Name %in% Beast.tree.NE.cluster3.subtree@phylo$tip.label) %>%
dplyr::group_by(Geo_Country) %>%
dplyr::summarise(Count=n())
# Separate metadata records show Hungarian sample "TPA_HUN180001" came from a male bisexual (MSWM).
Examine SNP scaled tree for distances
# Extract information about SNP distances
TPA.NEcluster3.pyjartree.mrca <- getMRCA(TPA.pyjar.tree, as.character(unlist(TPA.meta2.1[TPA.meta2.1$Sample_Name %in% Beast.tree.NE.cluster3.subtree@phylo$tip.label,"Sample_Name"])))
TPA.NEcluster3.pyjartree.subtree <- tree_subset(TPA.pyjar.tree, node=TPA.NEcluster3.pyjartree.mrca, levels_back=1)
ggtree(TPA.NEcluster3.pyjartree.subtree) + geom_tiplab(size=theme.text.size.within)

ggtree(TPA.NEcluster3.pyjartree.subtree)$data
Do some analysis of nearest neighbour and distances to MRCAs
calculate.years.from.mrca <- function(current.ggtree.phylo, current.ggtree.data){
#current.ggtree <- Beast.tree.NE.cluster3.subtree
all.tips <- current.ggtree.phylo$tip.label
dist.2.mrca <- NULL
### put dates into df
current.ggtree.data$mrca.median <- 2019.5 - current.ggtree.data$height_median
current.ggtree.data$year <- as.numeric(round(2019.5 - current.ggtree.data$height_median,3))
current.ggtree.data$mrca.95high <- round(2019.5 - sapply(1:nrow(current.ggtree.data),function(x) as.numeric(unlist(current.ggtree.data[x,"height_0.95_HPD"]))[1]), 3)
current.ggtree.data$mrca.95low <- round(2019.5 - sapply(1:nrow(current.ggtree.data),function(x) as.numeric(unlist(current.ggtree.data[x,"height_0.95_HPD"]))[2]), 3)
# extract dates between sample and its MRCA using loop
for (current.node in all.tips) {
current.parent <- c(match(current.node,current.ggtree.phylo$tip.label), phangorn::Ancestors(current.ggtree.phylo, match(c(current.node), current.ggtree.phylo$tip.label), "parent"))
current.nodelist <- current.ggtree.data[current.ggtree.data$node %in% current.parent,]
current.dist.2.mrca <- c(current.node, as.numeric(current.nodelist[1,"year"]-current.nodelist[2,"year"]))
dist.2.mrca <- rbind(dist.2.mrca, current.dist.2.mrca)
}
dist.2.mrca <- data.frame(Sample_Name=as.character(dist.2.mrca[,1]), dist.to.mrca=as.numeric(dist.2.mrca[,2]), stringsAsFactors=F)
return(dist.2.mrca)
}
### All samples in global tree
dist.mrca.all.TPA <- calculate.years.from.mrca(full.beast2.tree@phylo, full.beast2.tree@data)
Merge dist2MRCA with metadata
PHE.metadata.linked.dist2mrca <- left_join(PHE.metadata.linked, dist.mrca.all.TPA, by="Sample_Name")
p.time2mrca.orientation <- ggplot(PHE.metadata.linked.dist2mrca, aes(gender_orientation, dist.to.mrca, color=gender_orientation)) +
geom_quasirandom(size=0.75, alpha=0.5) +
theme_light() + theme.text.size +
coord_flip() +
labs(x="Gender Orientation", y="Years to MRCA", color="Gender Orientation") +
theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
scale_color_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation)
p.time2mrca.phe_region <- ggplot(PHE.metadata.linked.dist2mrca, aes(phe_centre, dist.to.mrca, color=phe_centre)) +
geom_quasirandom(size=0.75, alpha=0.5) +
theme_light() + theme.text.size +
coord_flip(ylim=c(0,40)) +
labs(x="UKHSA Region", y="Years to MRCA", color="UKHSA Region") +
theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
scale_color_manual(name="UKHSA\nRegion", values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region)
p.time2mrca.phe_region.orientation <- ggplot(PHE.metadata.linked.dist2mrca, aes(phe_centre, dist.to.mrca, color=gender_orientation)) +
geom_quasirandom(size=0.75, alpha=0.5) +
theme_light() + theme.text.size +
coord_flip(ylim=c(0,20)) +
labs(x="UKHSA Region", y="Years to MRCA") +
theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
scale_color_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation)
p.time2mrca.phe_region.orientation
Warning: Removed 15 rows containing missing values (position_quasirandom).

p.time2mrca.sublineage <- ggplot(PHE.metadata.linked.dist2mrca, aes(TPA.pinecone.sublineage, dist.to.mrca, color=TPA.pinecone.sublineage)) +
geom_quasirandom(size=0.75, alpha=0.5) +
theme_light() + theme.text.size +
coord_flip() +
labs(x="TPA Lineage", y="Years to MRCA", color="TPA Lineage") +
theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
scale_color_manual(values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage)
p.time2mrca.sublineage
Warning: Removed 15 rows containing missing values (position_quasirandom).

p.time2mrca.Lineage <- ggplot(PHE.metadata.linked.dist2mrca, aes(TPA_Lineage, dist.to.mrca, color=TPA_Lineage)) +
geom_quasirandom(size=0.75, alpha=0.5) +
theme_light() + theme.text.size +
coord_flip() +
labs(x="TPA Lineage", y="Years to MRCA (Median of Posterior)", color="TPA Lineage") +
theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
scale_color_manual(values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage)
Maybe can make an MST of the North East samples for grapetree?
TPA.pyjar.tree.subset.NorthEast <- ape::keep.tip(TPA.pyjar.tree, as.character(unlist(PHE.metadata.linked[PHE.metadata.linked$phe_centre=="North East","Sample_Name"])))
ggtree(TPA.pyjar.tree.subset.NorthEast)

#write.tree(TPA.pyjar.tree.subset.NorthEast, paste0(Data_input_directory,"TPA.UK-only-NorthEast.pyjar.2022-02-26.tre"))
# Write out a metadata sheet for the relevant information
PHE.metadata.linked.grapetree <- PHE.metadata.linked[,c("Sample_Name", "year","gender_orientation","phe_centre","hivpos","ukborn","TPA_Lineage","TPA.pinecone.sublineage")]
colnames(PHE.metadata.linked.grapetree)[1] <- "ID"
#write.table(PHE.metadata.linked.grapetree, paste0(Data_input_directory,"TPA.UK-only.grapetree.meta.2022-02-03.tsv"), sep = "\t", quote=F, row.names = F)
Alternative approach using MST instead of networks for North East data
# Read in MST
#TPA.NorthEastEngland.Grapetree.file <- paste0(Data_input_directory,"TPA-UK-NorthEast-2022-02-26.GenderOrientation-MSTree.inkscaped.+node-counts+GBMSM.svg")
p.TPA.NorthEastEngland.Grapetree <- ggdraw() + draw_image(TPA.NorthEastEngland.Grapetree.file)
p.TPA.NorthEastEngland.Grapetree

p.TPA.NorthEastEngland.Grapetree.header <- plot_grid(p.TPA.NorthEastEngland.Grapetree, labels=c("A - Network Clusters (North East England)"), label_size=panel.lab.size, scale=0.95)
Plot with beast trees
#p.PHE.NorthEast_MST.with.beast.subtrees.combi <- plot_grid(p.TPA.NorthEastEngland.Grapetree, p.Beast.tree.NE.subtrees.combi3, ncol=1, rel_heights=c(3,6), labels=c("A - Network Clusters (North East England)", ""), label_size=panel.lab.size, scale = 0.95)
p.PHE.NorthEast_MST.with.beast.subtrees.combi <- plot_grid(p.TPA.NorthEastEngland.Grapetree.header, p.Beast.tree.NE.subtrees.combi3, ncol=1, rel_heights=c(3,7))
p.PHE.NorthEast_MST.with.beast.subtrees.combi

#ggsave(paste0(Figure_output_directory,"Fig3_Sublin1.NorthEast.MST+Beast.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=200, height=245, device='pdf', dpi=1200)
Do some analysis of major sublineages over time by region - could this influence observations about sublineages?
# Generate some stats by PHE Region
PHE.major.sublineage.PHEcentre.date <- PHE.metadata.linked %>%
dplyr::filter(TPA.pinecone.sublineage %in% c(1,14)) %>%
dplyr::group_by(TPA.pinecone.sublineage, phe_centre, year) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
dplyr::arrange(desc(phe_centre), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'phe_centre'. You can override using the `.groups` argument.
ggplot(PHE.major.sublineage.PHEcentre.date, aes(year, phe_centre, size=Count, color=TPA.pinecone.sublineage)) +
geom_point() +
facet_grid(.~TPA.pinecone.sublineage) +
theme_light() +
theme.text.size +
scale_color_manual(values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage)

p.PHE.major.sublineage.PHEcentre.date.bubbleplot <- ggplot(PHE.major.sublineage.PHEcentre.date, aes(year, TPA.pinecone.sublineage, color=TPA.pinecone.sublineage)) +
geom_point(alpha=0.65, aes(size=Count)) +
geom_line(alpha=0.25) +
facet_grid(factor(gsub("\\ ","\n",phe_centre), levels=gsub("\\ ","\n",PHE.region.cols.brew$UKHSA.region))~., switch='y') +
theme_light() +
theme(strip.placement = "outside") +
theme(strip.background = element_rect(color='white', fill='white',linetype="solid"), strip.text.y=element_text(color = "grey25",angle=0, size=5)) +
scale_size_area(max_size = 4.5,breaks=c(1,5,10,20,30,40)) +
theme.text.size +
scale_color_manual(values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage) +
labs(y="Region", x="Year", color="Sublineage")
p.PHE.major.sublineage.PHEcentre.date.bubbleplot
geom_path: Each group consists of only one observation. Do you need to adjust the group aesthetic?

Do some specific analysis for the 3 Northern regions
# Generate some stats by PHE Region
PHE.metadata.linked %>%
dplyr::filter(phe_centre %in% c("North East", "North West", "Yorkshire and Humber")) %>%
dplyr::summarise(count=n())
PHE.metadata.linked %>%
dplyr::filter(phe_centre %in% c("North East", "North West", "Yorkshire and Humber")) %>%
dplyr::group_by(year) %>%
dplyr::summarise(count=n())
p.PHE.major.sublineage.3NorthernRegions <- PHE.metadata.linked %>%
dplyr::filter(phe_centre %in% c("North East", "North West", "Yorkshire and Humber")) %>%
dplyr::group_by(TPA.pinecone.sublineage, year, phe_centre) %>%
dplyr::summarise(Count=n()) %>%
ggplot(aes(year, Count, fill=phe_centre)) +
geom_bar(stat='identity', width=0.65) +
scale_fill_manual(values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region) +
theme_bw() + theme.text.size +
scale_x_continuous(breaks=seq(2012,2018,1)) +
scale_y_continuous(breaks=pretty) +
labs(title="Samples in 3 Northern Regions", x="Collection Year", y="Sample Count", fill="Public Health\nRegion") +
theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
#geom_text(aes(x=year,y=Count-0.5, label=Count), color='grey95', size=theme.text.size.within) +
NULL
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'year'. You can override using the `.groups` argument.
p.PHE.major.sublineage.3NorthernRegions

Single linkage network of identical genomes from UK
# Constrain by SNP distance (identical in the asr snp tree)
PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta[PHE.alignment.data.dist.melt.meta$Distance.Phylo==0,]
# and a max of 2 years
#PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta.identicals[PHE.alignment.data.dist.melt.meta.identicals$decimal.date.distance<=2,]
# And make sure that we actually have genetic distance data for all samples within the network
PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta.identicals[!is.na(PHE.alignment.data.dist.melt.meta.identicals$Distance.Phylo),]
# remove self-samples
PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta.identicals[PHE.alignment.data.dist.melt.meta.identicals$same.sample=="different",]
# cleanup some data noise
PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta.identicals[!is.na(PHE.alignment.data.dist.melt.meta.identicals$year.t1),]
# prepare intput data (with edge info)
PHE.alignment.data.dist.melt.meta.identicals.input1 <- PHE.alignment.data.dist.melt.meta.identicals[,c("Taxa1","Taxa2","Distance.Phylo","decimal.date.distance","year.distance","Orientation.Class","epi.time.distance.cat.years","epi.time.distance.cat")]
############
# some issues with update to R4 - double sided matrix
PHE.alignment.data.dist.melt.meta.identicals.input1$edgename <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta.identicals.input1), function(x) paste0(sort(as.character(unlist(PHE.alignment.data.dist.melt.meta.identicals.input1[x,c("Taxa1","Taxa2")]))),collapse="___"))
PHE.alignment.data.dist.melt.meta.identicals.input1 <- PHE.alignment.data.dist.melt.meta.identicals.input1[!duplicated(PHE.alignment.data.dist.melt.meta.identicals.input1$edgename),]
# Also having an issue with taxa as factors here
PHE.alignment.data.dist.melt.meta.identicals.input1$Taxa1 <- as.character(PHE.alignment.data.dist.melt.meta.identicals.input1$Taxa1)
PHE.alignment.data.dist.melt.meta.identicals.input1$Taxa2 <- as.character(PHE.alignment.data.dist.melt.meta.identicals.input1$Taxa2)
############
# Deduplicate
#inverse weight
PHE.alignment.data.dist.melt.meta.identicals.input1$decimal.date.distance.inv <- 1/1/(PHE.alignment.data.dist.melt.meta.identicals.input1$decimal.date.distance+0.04)
# Make actual network
set.seed(1236)
PHE.identicals.network <- network(PHE.alignment.data.dist.melt.meta.identicals.input1, matrix.type = "edgelist", ignore.eval = FALSE, directed = F, loops = F)
#PHE.identicals.network.gg <- ggnetwork(PHE.identicals.network, layout = "kamadakawai", weights = "decimal.date.distance.inv")
#PHE.identicals.network.gg <- ggnetwork(PHE.identicals.network, layout = "fruchtermanreingold", weights = "decimal.date.distance")
PHE.identicals.network.gg <- ggnetwork(PHE.identicals.network, layout = "fruchtermanreingold")
PHE.identicals.network.gg$Taxa1 <- PHE.identicals.network.gg$vertex.names
# extract temporal clusters from network
PHE.identicals.network.ig <- asIgraph(PHE.identicals.network)
PHE.identicals.network.components <- data.frame(Taxa1=network.vertex.names(PHE.identicals.network), vertex.no=as.vector(V(PHE.identicals.network.ig)), cluster=igraph::components(PHE.identicals.network.ig)$membership)
PHE.identicals.network.components$Cluster <- paste0("Cluster",PHE.identicals.network.components$cluster)
# merge metadata back in
PHE.identicals.network.gg <- plyr::join(PHE.identicals.network.gg, data.frame(Taxa1=PHE.metadata.linked$Sample_Name, PHE.metadata.linked[,c("phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], stringsAsFactors = F),by="Taxa1", type="left")
PHE.identicals.network.gg <- plyr::join(PHE.identicals.network.gg, data.frame(Taxa1=PHE.identicals.network.components$Taxa1, Cluster=PHE.identicals.network.components$Cluster), by="Taxa1", type="left")
#
# Add temporal colour scale
#unique(PHE.identicals.network.gg$epi.time.distance.cat)
epi.time.distance.cat.cols <- rev(colorRampPalette(brewer.pal(8, "Greys"))(length(unique(PHE.identicals.network.gg$epi.time.distance.cat))-1))
# Plot network
p.PHE.identicals.network.0SNP <- ggplot(PHE.identicals.network.gg, aes(x = x, y = y, xend = xend, yend = yend)) +
geom_edges(alpha=0.90, curvature = 0.2, aes(color=factor(epi.time.distance.cat), linetype=factor(epi.time.distance.cat))) +
#scale_color_manual(values=c("grey5","grey35","grey55", "grey65", "grey75"), name="SNP\nDistance") +
scale_color_manual(name="Temporal\nDistance", values = epi.time.distance.cat.cols) +
scale_linetype(name="Temporal\nDistance") +
theme_blank() +
ggnewscale::new_scale_color() + ggnewscale::new_scale("size") +
#geom_nodelabel(aes(color=gender_orientation, label=paste(Taxa1,year,sep="\n"),fontface = "bold"), alpha=0.8, size=theme.text.size.within-0.4, label.size=0.15, label.padding = unit(0.05, "lines")) +
geom_nodes(size=2.5, aes(color=gender_orientation), alpha=0.9) +
scale_color_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
NULL
p.PHE.identicals.network.0SNP

Plot this against a UK tree?
gheatmap(ggtree(TPA.pyjar.tree.subset.uk),
data.frame(row.names=PHE.identicals.network.components$Taxa1, Cluster=PHE.identicals.network.components$Cluster))

Some stats from this
p.PHE.identical.Orientation_class.bydatedist <- PHE.alignment.data.dist.melt.meta %>%
dplyr::filter(same.sample=="different", Distance.Phylo==0) %>%
#filter(decimal.date.distance<=1) %>%
dplyr::group_by(epi.time.distance.cat, Orientation.Class) %>%
dplyr::summarise(Count.class.date=n()) %>%
dplyr::mutate(sum.class=sum(Count.class.date), fract.class=Count.class.date/sum.class) %>%
ggplot(aes(x=epi.time.distance.cat, y=Count.class.date, fill=Orientation.Class)) +
geom_bar(stat='identity', position='stack') +
theme_bw() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
labs(x="Time between samples", y="Interaction Count", fill="Orientation Type")
`summarise()` has grouped output by 'epi.time.distance.cat'. You can override using the `.groups` argument.
p.PHE.identical.Orientation_class.bydatedist

p.PHE.identical.Orientation_class.byZerodist.cluster <- PHE.identicals.network.gg %>%
dplyr::filter(!is.na(Orientation.Class)) %>%
dplyr::group_by(Cluster, Orientation.Class) %>%
dplyr::summarise(Count.class.cluster=n()) %>%
dplyr::mutate(sum.class=sum(Count.class.cluster), fract.class=Count.class.cluster/sum.class) %>%
dplyr::arrange(desc(sum.class)) %>%
dplyr::ungroup() %>%
dplyr::mutate(Cluster=as_factor(Cluster)) %>%
ggplot(aes(x=Cluster, y=Count.class.cluster, fill=Orientation.Class)) +
geom_bar(stat='identity', position='stack') +
theme_bw() +
x.theme.axis.rotate +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
labs(x="Identical Genome Cluster", y="Interaction Count", fill="Orientation Type")
`summarise()` has grouped output by 'Cluster'. You can override using the `.groups` argument.
p.PHE.identical.Orientation_class.byZerodist.cluster

d.PHE.identical.GenderOrientation.byZerodist.cluster <- left_join(PHE.identicals.network.components[,c("Taxa1","Cluster")], PHE.metadata.linked[,c("Sample_Name","phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], by=c("Taxa1"="Sample_Name")) %>%
dplyr::group_by(TPA.pinecone.sublineage, Cluster, gender_orientation) %>%
dplyr::summarise(count.orient.cluster=n()) %>%
dplyr::mutate(count.cluster=sum(count.orient.cluster), fract=count.orient.cluster/count.cluster) %>%
dplyr::ungroup() %>%
dplyr::arrange(desc(count.cluster)) %>%
dplyr::mutate(Cluster.o=as_factor(Cluster))
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'Cluster'. You can override using the `.groups` argument.
# Plot sample counts by genome cluster (coloured by orientation)
p.PHE.identical.GenderOrientation.byZerodist.cluster <- d.PHE.identical.GenderOrientation.byZerodist.cluster %>%
ggplot(aes(Cluster.o, count.orient.cluster, fill=gender_orientation)) +
geom_bar(stat="identity", width=0.65) +
scale_fill_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation, guide = guide_legend(order = 1)) +
theme_light() +
x.theme.axis.rotate +
scale_y_continuous(breaks=seq(0,45,5)) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
labs(x="Identical Genome Cluster", y="Sample Count", fill="Patient Gender Orientation")
# Add details of sublineage
p.PHE.identical.GenderOrientation.byZerodist.cluster <- p.PHE.identical.GenderOrientation.byZerodist.cluster +
ggnewscale::new_scale_color() +
geom_point(data=(d.PHE.identical.GenderOrientation.byZerodist.cluster %>% select(Cluster.o, TPA.pinecone.sublineage) %>% distinct()), aes(Cluster.o, -1.5, color=TPA.pinecone.sublineage), inherit.aes = F) + scale_color_manual(values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage, name="Sublineage", guide = guide_legend(order = 2)) +
NULL
# Add a sublineage axis label (bit of a hack)
p.PHE.identical.GenderOrientation.byZerodist.cluster <- p.PHE.identical.GenderOrientation.byZerodist.cluster +
geom_text(data=data.frame(lab="Sublineage", y=-1.5, x=28, stringsAsFactors=F), aes(label=lab, x=x, y=y), hjust = 0.1, size=theme.text.size.within, inherit.aes = F) +
coord_cartesian(x=c(1, 27), clip='off')
p.PHE.identical.GenderOrientation.byZerodist.cluster

#ggsave(paste0(Figure_output_directory,"SupFig6_Identical-SNP-clust_orientation.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=120, height=100, device='pdf', dpi=1200)
Possible to introduce some more info into that plot?
d.PHE.identical.region.byZerodist.cluster <- left_join(PHE.identicals.network.components[,c("Taxa1","Cluster")], PHE.metadata.linked[,c("Sample_Name","phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], by=c("Taxa1"="Sample_Name")) %>%
dplyr::group_by(TPA.pinecone.sublineage, Cluster, phe_centre) %>%
dplyr::summarise(count.region.cluster=n()) %>%
dplyr::mutate(count.cluster=sum(count.region.cluster), fract=count.region.cluster/count.cluster) %>%
dplyr::ungroup() %>%
dplyr::arrange(desc(count.cluster)) %>%
dplyr::mutate(Cluster.o=as_factor(Cluster))
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'Cluster'. You can override using the `.groups` argument.
p.PHE.identical.Region.byZerodist.cluster <- d.PHE.identical.region.byZerodist.cluster %>%
ggplot(aes(Cluster.o, count.region.cluster, fill=phe_centre)) +
geom_bar(stat="identity", width=0.65, position='fill') +
scale_fill_manual(name="UKHSA\nRegion", values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region, guide = guide_legend(order = 1)) +
theme_light() +
x.theme.axis.rotate +
scale_y_continuous(breaks=seq(0,45,5)) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
guides(fill=guide_legend(ncol=2)) +
labs(x="Identical Genome Cluster", y="Region Proportion", fill="UKHSA Region")
p.PHE.identical.byZerodist.cluster.barcombi <- plot_grid(p.PHE.identical.GenderOrientation.byZerodist.cluster + x.theme.strip, p.PHE.identical.Region.byZerodist.cluster, ncol=1, axis="rlt", align=T, rel_heights = c(2,1), labels=c("B","C"), label_size=panel.lab.size)
#p.PHE.identical.byZerodist.cluster.barcombi
#p.PHE.identicals.network.0SNP
plot_grid(p.PHE.identicals.network.0SNP, p.PHE.identical.byZerodist.cluster.barcombi, ncol=1, rel_heights=c(2,3), labels=c("A",""), label_size=panel.lab.size)

PHE.identicals.network.gg.region.scatterpie.groups <- PHE.identicals.network.gg %>%
dplyr::select(Cluster, Taxa1, phe_centre) %>%
dplyr::distinct() %>%
dplyr::group_by(Cluster, phe_centre) %>%
dplyr::summarise(Count.centre=n()) %>%
dplyr::mutate(x=Cluster, y=3.5) %>%
pivot_wider(names_from="phe_centre", values_from="Count.centre", values_fill=0) %>%
dplyr::select(Cluster,x,y,unique(PHE.identicals.network.gg$phe_centre)) %>%
dplyr::ungroup() %>%
dplyr::mutate(Cluster.numeric=as.numeric(1:27))
`summarise()` has grouped output by 'Cluster'. You can override using the `.groups` argument.
p.PHE.identical.GenderOrientation.byZerodist.cluster +
ggnewscale::new_scale_fill() #+

NA
NA
Get a few more stats on the largest cluster (Cluster 8)
#d.PHE.identical.GenderOrientation.byZerodist.cluster %>% filter(Cluster=="Cluster8")
PHE.identicals.network.gg.identical.cluster8 <- PHE.identicals.network.gg %>% filter(Cluster=="Cluster8") %>%
select(vertex.names, Orientation.Class, phe_centre, year, TPA_Lineage, TPA.pinecone.sublineage, hivpos, Cluster)
sort(unique(PHE.identicals.network.gg.identical.cluster8$year))
[1] 2012 2013 2014 2015 2016 2017 2018
Get some more information about the heterosexual only clusters
PHE.identicals.network.gg.identical_heteroclusters <- PHE.identicals.network.gg %>% filter(Cluster %in% c("Cluster12", "Cluster20", "Cluster27")) %>%
select(vertex.names, Cluster, gender_orientation, phe_centre, year, TPA_Lineage, TPA.pinecone.sublineage, hivpos) %>%
distinct() %>%
arrange(Cluster, year, gender_orientation)
PHE.identicals.network.gg.identical_heteroclusters
And do the same for the small mixed/GBMSM clusters
PHE.identicals.network.gg.identical_not.heteroclusters <- PHE.identicals.network.gg %>% filter(Cluster %notin% c("Cluster12", "Cluster20", "Cluster27", "Cluster8")) %>%
select(vertex.names, Cluster, gender_orientation, phe_centre, year, TPA_Lineage, TPA.pinecone.sublineage, hivpos) %>%
distinct() %>%
arrange(Cluster, year, gender_orientation)
PHE.identicals.network.gg.identical_not.heteroclusters
What proportion of heterosexuals have an identical GBMSM paired genome?
# Delineate heterosexual clusters
d.PHE.identical.heterosexual.clusters <- d.PHE.identical.GenderOrientation.byZerodist.cluster %>%
dplyr::mutate(is.heterosexual=ifelse(gender_orientation%in% c("MSW", "WSM"), "heterosexual", ifelse(gender_orientation=="GBMSM","GBMSM", "Unknown"))) %>%
dplyr::group_by(Cluster,is.heterosexual) %>%
dplyr::mutate(count.hetero=sum(count.orient.cluster), fract.hetero=sum(count.orient.cluster)/count.cluster) %>%
dplyr::ungroup() %>%
dplyr::filter(is.heterosexual=="heterosexual") %>%
dplyr::select(-c(count.orient.cluster, gender_orientation, fract)) %>%
dplyr::distinct() %>%
dplyr::mutate(cluster.type=ifelse(fract.hetero==1, "hetero.only", "other"))
d.PHE.identical.heterosexual.clusters
# What proportion of heterosexuals (n=20) are in a heterosexual-only cluster?
d.PHE.identical.heterosexual.clusters %>%
dplyr::group_by(cluster.type) %>%
dplyr::summarise(count.in.hetero.cluster=sum(count.hetero)) %>%
dplyr::mutate(fract.in.hetero=count.in.hetero.cluster/sum(count.in.hetero.cluster))
#left_join(PHE.identicals.network.components[,c("Taxa1","Cluster")], PHE.metadata.linked[,c("Sample_Name","phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], by=c("Taxa1"="Sample_Name"))
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayAtIFRyZXBvbmVtYSBVSyBVS0hTQS1jb2hvcnQgQW5hbHlzaXMgMjAyMiIKb3V0cHV0OgogIHBkZl9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKTWFrZSBhIGNsZWFuIGVudmlyb25tZW50CmBgYHtyfQogcm0obGlzdD1scygpKQpgYGAKXApMb2FkIHBhY2thZ2VzCmBgYHtyfQpwYWNrYWdlcy5saXN0IDwtIGMoImdncGxvdDIiLCJ0cmVlaW8iLCJnZ3RyZWUiLCJnZ25ld3NjYWxlIiwiYXBlIiwiZHBseXIiLCJ0aWR5dmVyc2UiLCJ0aWR5ciIsInBoeXRvb2xzIiwiUkNvbG9yQnJld2VyIiwibHVicmlkYXRlIiwicmVhZHhsIiwiZ2dmb3JjZSIsImdnc3RhbmNlIiwiZ2dyaWRnZXMiLCJjb3dwbG90IiwiaGV4YmluIiwic2NhbGVzIiwiaGF2ZW4iLCJuZXR3b3JrIiwiZ2duZXR3b3JrIiwiaW50ZXJncmFwaCIsImlncmFwaCIsImdncmFwaCIsImdyYXBobGF5b3V0cyIsInNjYXR0ZXJwaWUiLCJtYXBzIiwibWFwZGF0YSIsIm1hcHRvb2xzIiwicmdkYWwiLCJyZ2VvcyIsImJyb29tIiwiZ2dyZXBlbCIsImdncmlkZ2VzIiwibWFnaWNrIiwiZ2diZWVzd2FybSIpCgojInBseXIiLCJDYWlybyIsImdnbWFwIiwiZW1vamlmb250IiwiclBpbmVjb25lIiwicGFpcnNucCIsIkNvb3JkaW5hdGVDbGVhbmVyIiwiZ3JpZEV4dHJhIiwiZGVuZGV4dGVuZCIsImdnZGVuZHJvIiwKCiNCaW9jTWFuYWdlcjo6aW5zdGFsbCgiZ2d0cmVlIikKI0Jpb2NNYW5hZ2VyOjppbnN0YWxsKCJ0cmVlaW8iKQoKZm9yKHBrZyBpbiBwYWNrYWdlcy5saXN0KXsKICBldmFsKGJxdW90ZShsaWJyYXJ5KC4ocGtnKSkpKSB9CmBgYApcCkNvbmZpcm0gY3VycmVudCBlbnZpcm9ubWVudGFsIHNldHVwCmBgYHtyfQpSLlZlcnNpb24oKQpwcmludChzZXNzaW9uSW5mbygpKQpgYGAKXApNYWtlIHNvbWUgc2hvcnRjdXRzIGZvciBwbG90dGluZyAKYGBge3J9CnkudGhlbWUuc3RyaXAgPC0gdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcy55PSBlbGVtZW50X2JsYW5rKCkpCnkudGhlbWUuc3RyaXAucGFydGlhbCA8LSB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcy55PSBlbGVtZW50X2JsYW5rKCkpCgp4LnRoZW1lLnN0cmlwIDwtIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueD0gZWxlbWVudF9ibGFuaygpKQp4LnRoZW1lLnN0cmlwLnBhcnRpYWwgPC0gdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueD0gZWxlbWVudF9ibGFuaygpKQp4LnRoZW1lLnN0cmlwLmxhYnMgPC0gdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKQoKeC50aGVtZS5heGlzLnJvdGF0ZSA8LSB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpCgpsZWdlbmQuc3RyaXAgPC0gdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKdGhlbWUudGV4dC5zaXplIDwtIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkKCiclbm90aW4lJyA8LSBOZWdhdGUoJyVpbiUnKQoKbWF4LmZvbnQuc2l6ZSA8LSA3CmJhc2ljLmZvbnQuc2l6ZSA8LSA2Cm1pbi5mb250LnNpemUgPC0gNS4yNQp0aGVtZS50ZXh0LnNpemUgPC0gdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gYmFzaWMuZm9udC5zaXplKSkKdGhlbWUudGV4dC5zaXplLndpdGhpbiA8LSAoNS8xNCkqbWluLmZvbnQuc2l6ZQpwYW5lbC5sYWIuc2l6ZSA8LSAxMAoKYGBgClwKU3BlY2lmeSByYXcgZGF0YSAtIGdsb2JhbCBkYXRhc2V0CmBgYHtyfQojRGF0YV9pbnB1dF9kaXJlY3RvcnkgPC0gIi9Vc2Vycy9tYjI5L1BhcGVycy9UcmVwb25lbWFfVUstUEhFLWdlbi1lcGlfMjAyMS9EYXRhLyIKI0RhdGFfaW5wdXRfZGlyZWN0b3J5IDwtICIvVXNlcnMvbWIyOS9QYXBlcnMvVHJlcG9uZW1hX1VLLVBIRS1nZW4tZXBpXzIwMjEvUm5vdGVib29rL1Jub3RlYm9va18wOS0yMDIyL2RhdGEvIgpEYXRhX2lucHV0X2RpcmVjdG9yeSA8LSBwYXN0ZTAoZ2V0d2QoKSwgIi9pbnB1dGRhdGEvIikKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyMjIFRyZWUgZGF0YSAKCiMgTUwgdHJlZSAocmVmaW5lZCBkYXRhc2V0KQpUUEEuTUx0cmVlLmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEtdWJlci5yZW1hc2tlZC4yMDIwLTExLTEwLmdvb2Rjb3YyNS5ndWJiaW5zLlNOUHMuYWxuLnJlbmFtZWQuZml4LXplcm8tZGlzdC50cmVlZmlsZSIpCgojIFB5amFyIHRyZWUgKHJlZmluZWQgZGF0YXNldCkKVFBBLnB5amFyLmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEtdWJlci5yZW1hc2tlZC4yMDIwLTExLTEwLmdvb2Rjb3YyNS5ndWJiaW5zLlNOUHMuYWxuLnJlbmFtZWQucHlqYXIudHJlIikKCiMgRnVsbCBzaXplIEJFQVNUMiBhbmFseXNpcyAtIHByZXZpb3VzbHkgZ2VuZXJhdGVkIGFzIHBhcnQgb2YgQmVhbGUsIDIwMjEuCmZ1bGwuYmVhc3QyLnRyZWUuZmlsZSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS11YmVyX2JlYXN0Ml9zdHJpY3Qtc2t5bGluZS01MDBNXzEwcG9wX2NvbnNlbnN1cy50cmVlIikKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyMjIE1ldGEgZGF0YSAKCiMgU3VwcGxlbWVudCBmcm9tIFRQQS1VYmVyIHBhcGVyIC0gQmVhbGUsIDIwMjEgClRQQS5tZXRhMi5maWxlIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiU3VwX0RhdGExX0dsb2JhbF9TYW1wbGUtTWV0YWRhdGFfXzA5LTIwMjIueGxzeCIpCgojIEVuZ2xhbmQgc3BlY2lmaWMgbWV0YWRhdGEgY29sbGF0ZWQgYnkgUEhFL1VLSFNBClBIRS5tZXRhZGF0YS5saW5rZWQuZmlsZSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlN1cF9EYXRhMl9UUEEuVUstb25seS5QSEUubWV0YWRhdGEuMjAyMi0wMi0wMi54bHN4IikKCiMgRW5nbGFuZCBzcGVjaWZpYyBtYXBwaW5nIHNoYXBlZmlsZSBkYXRhIHdpdGggUHVibGljIEhlYWx0aCBCb3VuZGFyaWVzCiMgSW1wb3J0ZWQgZGF0YWZpbGUgZnJvbSBodHRwczovL2dlb3BvcnRhbC5zdGF0aXN0aWNzLmdvdi51ay9kYXRhc2V0cy9wdWJsaWMtaGVhbHRoLWVuZ2xhbmQtY2VudHJlcy1kZWNlbWJlci0yMDE2LWZ1bGwtY2xpcHBlZC1ib3VuZGFyaWVzLWluLWVuZ2xhbmQvZXhwbG9yZT9sb2NhdGlvbj01Mi45NTAwMDAlMkMtMi4wMDAwMDAlMkM2Ljg4ClVLLnB1YmxpY2hlYWx0aC5zaGFwZWZpbGUuZGF0YSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlB1YmxpY19IZWFsdGhfRW5nbGFuZF9DZW50cmVzXyhEZWNlbWJlcl8yMDE2KV9Cb3VuZGFyaWVzIikKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyMjIEV4dGVybmFsbHkgcGxvdHRlZCBmaWd1cmVzIChlLmcuIEdyYXBlVHJlZSkgZm9yIGluY2x1c2lvbiBpbiBtdWx0aXBhbmVsIGZpZ3VyZXMKCiMgRXh0ZXJuYWxseSBwbG90dGVkIGdyYXBldHJlZSBtaW5pbXVtIHNwYW5uaW5nIHRyZWUgZm9yIHdob2xlIG9mIEVuZ2xhbmQgLSBjb2RlIHRvIGV4dHJhY3Qgc3VidHJlZSB0aGF0IHdhcyB1c2VkIHRvIG1ha2UgdGhpcyBpcyBpbmNsdWRlZCBsYXRlciBpbiB0aGlzIFJub3RlYm9vawpUUEEuVUsuR3JhcGV0cmVlLnN1YmxpbmVhZ2VzLmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEtVUstMjAyMi0wMi0wMy5zdWJsaW5lYWdlLU1TVHJlZS5JbmtzY2FwZWQuc3ZnIikKCiMgRXh0ZXJuYWxseSBwbG90dGVkIGdyYXBldHJlZSBtaW5pbXVtIHNwYW5uaW5nIHRyZWUgZm9yIHdob2xlIG9mIEVuZ2xhbmQgLSAzLXZhcmlhYmxlIHBsb3RzClRQQS5VSy5HcmFwZXRyZWUuM3dheS5maWxlIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiVFBBLVVLLTIwMjItMDItMTYuLU1TVHJlZV8zLXdheS1maWd1cmUuSW5zY2FwZWQtMy5zdmciKQoKIyBFeHRlcm5hbGx5IHBsb3R0ZWQgZ3JhcGV0cmVlIG1pbmltdW0gc3Bhbm5pbmcgdHJlZSBmb3Igd2hvbGUgb2YgRW5nbGFuZCAtIEhJViBzdGF0dXMKVFBBLlVLLkdyYXBldHJlZS5ISVYuZmlsZSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS1VSy0yMDIyLTAyLTAzLkhJVnN0YXR1cy1NU1RyZWVfaW5rc2NhcGVkLnN2ZyIpCgojIEV4dGVybmFsbHkgcGxvdHRlZCBncmFwZXRyZWUgbWluaW11bSBzcGFubmluZyB0cmVlIGZvciBOb3J0aCBFYXN0IEVuZ2xhbmQgbmV0d29ya3MKVFBBLk5vcnRoRWFzdEVuZ2xhbmQuR3JhcGV0cmVlLmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEtVUstTm9ydGhFYXN0LTIwMjItMDItMjYuR2VuZGVyT3JpZW50YXRpb24tTVNUcmVlLmlua3NjYXBlZC4rbm9kZS1jb3VudHMrR0JNU00uc3ZnIikKCgpgYGAKXApTcGVjaWZ5IGRpcmVjdG9yeSB0byBvdXRwdXQgcGxvdHMKYGBge3J9CkZpZ3VyZV9vdXRwdXRfZGlyZWN0b3J5IDwtIHBhc3RlMChnZXR3ZCgpLCAiL0ZpZ3VyZXMvIikKCiMiL1VzZXJzL21iMjkvUGFwZXJzL1RyZXBvbmVtYV9VSy1QSEUtZ2VuLWVwaV8yMDIxL0ZpZ3VyZXMvRmlndXJlX0RyYWZ0aW5nL1dvcmtpbmdfRmlndXJlc18wOC0yMDIyLyIKYGBgClwKUmVhZCBpbiB0cmVlcwpgYGB7cn0KVFBBLk1MdHJlZSA8LSBtaWRwb2ludC5yb290KHJlYWQudHJlZShUUEEuTUx0cmVlLmZpbGUpKQpUUEEucHlqYXIudHJlZSA8LSBtaWRwb2ludC5yb290KHJlYWQudHJlZShUUEEucHlqYXIuZmlsZSkpCmBgYApcClJlYWQgaW4gZmluYWwgb3V0cHV0IG1ldGFkYXRhIGZyb20gR2xvYmFsIFViZXIgc3R1ZHkgKEJlYWxlIDIwMjEpCmBgYHtyfQpUUEEubWV0YTIuMSA8LSByZWFkeGw6OnJlYWRfZXhjZWwoVFBBLm1ldGEyLmZpbGUsc2hlZXQ9IlN1cHBsZW1lbnRhcnlfRGF0YTFfU2FtcGxlLU1ldGEiKQpgYGAKXApDcmVhdGUgYSBjb2xvdXIgc2NoZW1lIGZvciBMaW5lYWdlcywgQ291bnRyaWVzIGFuZCBDb250aW5lbnRzIChjb25zaXN0ZW50IHdpdGggQmVhbGUsIDIwMjEpCmBgYHtyfQojIENvbG91cmluZyBmb3IgY291bnRyeQpjb250aW5lbnRhbC5jb3VudHJ5LmNvbHMuYnJldzIgPC0gdW5pcXVlKFRQQS5tZXRhMi4xWyxjKCJHZW9fQ291bnRyeSIsIkNvbnRpbmVudCIpXSkKY29udGluZW50YWwuY291bnRyeS5jb2xzLmJyZXcyIDwtIGNvbnRpbmVudGFsLmNvdW50cnkuY29scy5icmV3MltvcmRlcihjb250aW5lbnRhbC5jb3VudHJ5LmNvbHMuYnJldzIkQ29udGluZW50LGNvbnRpbmVudGFsLmNvdW50cnkuY29scy5icmV3MiRHZW9fQ291bnRyeSksXQoKY29udGluZW50YWwuY291bnRyeS5jb2xzLmJyZXcyJGNvdW50cnkuY29sIDwtIGMoIiNlYzcwMTQiLCIjZmVjNDRmIiwiI2RlMmQyNiIsIiNmYjZhNGEiLCIjYmRiZGJkIiwiIzczNzM3MyIsYnJld2VyLnBhbChuPTgsIlB1cnBsZXMiKVs0OjhdLGJyZXdlci5wYWwobj04LCJCbHVlcyIpWzM6OF0sYnJld2VyLnBhbChuPTUsIkdyZWVucyIpWzM6NV0sIiNjNTFiOGEiLCIjOGM1MTBhIikKCiMgQ29sb3VyaW5nIGZvciBDb250aW5lbnQKY29udGluZW50YWwuY29scy5icmV3MiA8LSBkYXRhLmZyYW1lKENvbnRpbmVudD1zb3J0KHVuaXF1ZShUUEEubWV0YTIuMSRDb250aW5lbnQpKSxzdHJpbmdzQXNGYWN0b3JzPUYpCmNvbnRpbmVudGFsLmNvbHMuYnJldzIkY29udGluZW50LmNvbCA8LSBjKCIjZmVjNDRmIiwiI2RlMmQyNiIsIiNiZGJkYmQiLCIjMjE3MWI1IiwiIzc0YzQ3NiIsIiNjNTFiOGEiLCIjZWM3MDE0IikKCgojIENvbG91cmluZyBmb3IgVFBBIExpbmVhZ2UKVFBBX0xpbmVhZ2UuY29scyA8LSBkYXRhLmZyYW1lKExpbmVhZ2U9c29ydCh1bmlxdWUoVFBBLm1ldGEyLjEkVFBBX0xpbmVhZ2UpKSxzdHJpbmdzQXNGYWN0b3JzPUYpClRQQV9MaW5lYWdlLmNvbHMkTGluZWFnZS5jb2wgPC0gYygicm95YWxibHVlMiIsICJpbmRpYW5yZWQxIikKI2MoIiM0MzZlZWUiLCAiIzY2NjY2NiIsIiNmZjZhNmEiKQpUUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UgPC0gZmFjdG9yKFRQQV9MaW5lYWdlLmNvbHMkTGluZWFnZSwgbGV2ZWxzPWMoIk5pY2hvbHMiLCJTUzE0Iiwib3V0bGllciIpKQoKIyBMaW5lYWdlIEhleGNvZGVzCiMgcm95YWxibHVlMiAjNDM2ZWVlCiMgaW5kaWFucmVkMSAjZmY2YTZhCmBgYApcCkRlZmluZSBjb2xvdXJzIGZvciBzdWJsaW5lYWdlcwpgYGB7cn0KIyBEZWZpbmUgc3VibGluZWFnZSBjbHVzdGVyaW5nIHNjaGVtZSB1c2luZyBicmV3IGNvbG91cnNjYWxlcwpzdWJsaW5lYWdlcy5jb2xzLmJyZXcgPC0gZGF0YS5mcmFtZSh1bmlxdWUoVFBBLm1ldGEyLjFbLGMoIlRQQV9MaW5lYWdlIiwiVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UiKV0pLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKc3VibGluZWFnZXMuY29scy5icmV3IDwtIHN1YmxpbmVhZ2VzLmNvbHMuYnJld1tvcmRlcihzdWJsaW5lYWdlcy5jb2xzLmJyZXckVFBBX0xpbmVhZ2Usc3VibGluZWFnZXMuY29scy5icmV3JFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSxdCgpzdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluLm9yZGVyIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHN1YmxpbmVhZ2VzLmNvbHMuYnJldyRUUEEucGluZWNvbmUuc3VibGluZWFnZSkpCnN1YmxpbmVhZ2VzLmNvbHMuYnJldyA8LSBzdWJsaW5lYWdlcy5jb2xzLmJyZXdbb3JkZXIoc3VibGluZWFnZXMuY29scy5icmV3JHN1Ymxpbi5vcmRlciksXQoKIyBGb3IgcmV2aXNlZCBib290c3RyYXBwZWQgY2x1c3RlcnMKc3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UuY29scyA8LSBjKCIjRkM5MjcyIiwiI0VGM0IyQyIsYnJld2VyLnBhbChuPTQsIkdyZWVucyIpWzI6NF0sYnJld2VyLnBhbChuPTQsIllsT3JCciIpW2MoMiwzKV0sYnJld2VyLnBhbChuPTYsIkJsdWVzIilbMjo2XSxicmV3ZXIucGFsKG49NiwiUHVycGxlcyIpWzI6Nl0sImdyZXk4MCIsImdyZXk4MCIsImdyZXk4MCIsImdyZXk4MCIpCiAgCnN1YmxpbmVhZ2VzLmNvbHMuYnJldyA8LSB1bmlxdWUoc3VibGluZWFnZXMuY29scy5icmV3WyxjKCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIsInN1YmxpbmVhZ2UuY29scyIpXSkKc3VibGluZWFnZXMuY29scy5icmV3IDwtIHN1YmxpbmVhZ2VzLmNvbHMuYnJld1tvcmRlcihhcy5udW1lcmljKGFzLmNoYXJhY3RlcihzdWJsaW5lYWdlcy5jb2xzLmJyZXckVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpKSksXQpzdWJsaW5lYWdlcy5jb2xzLmJyZXckVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UgPC0gZmFjdG9yKHN1YmxpbmVhZ2VzLmNvbHMuYnJldyRUUEEucGluZWNvbmUuc3VibGluZWFnZSwgbGV2ZWxzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRUUEEucGluZWNvbmUuc3VibGluZWFnZSkKc3VibGluZWFnZXMuY29scy5icmV3IDwtIHN1YmxpbmVhZ2VzLmNvbHMuYnJld1shaXMubmEoc3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UpLF0KCmNvbG5hbWVzKHN1YmxpbmVhZ2VzLmNvbHMuYnJldykgPC0gYygic3VibGluZWFnZSIsInN1YmxpbmVhZ2UuY29scyIpCnN1YmxpbmVhZ2VzLmNvbHMuYnJldyA8LSB1bmlxdWUoc3VibGluZWFnZXMuY29scy5icmV3KQpgYGAKXApSZXN0cmljdCBhbmFseXNpcyB0byBoaWdoIHF1YWxpdHkgZ2Vub21lcyAoYW5kIHRyZWUpCmBgYHtyfQpUUEEubWV0YTIuMSA8LSBUUEEubWV0YTIuMVtUUEEubWV0YTIuMSRmaW5lc2NhbGUuYW5hbHlzaXM9PSJZZXMiLF0KYGBgClwKQ3JlYXRlIGEgIlVLIiB2YXJpYWJsZSwgYW5kIGEgIlBIRSIgdmFyaWFibGUKYGBge3J9ClRQQS5tZXRhMi4xJGlzLlVLIDwtIGlmZWxzZShUUEEubWV0YTIuMSRHZW9fQ291bnRyeT09IlVLIiwiVUsiLCJPdGhlciIpClRQQS5tZXRhMi4xJGlzLlBIRSA8LSBpZmVsc2UoVFBBLm1ldGEyLjEkR2VvX0NvdW50cnk9PSJVSyIgJiBncmVwbCgiUEhFIixUUEEubWV0YTIuMSRTYW1wbGVfTmFtZSksIlBIRSIsIk90aGVyIikKYGBgClwKYGBge3J9CiMgUHJlcGFyZSBNTCB0cmVlClRQQS5NTHRyZWUuZ2d0cmVlIDwtIGdndHJlZShUUEEuTUx0cmVlLGxheW91dCA9ICJmYW4iLG9wZW4uYW5nbGUgPSAxMCwgcmlnaHQ9VCkKCiMgUHJlcGFyZSBjb3VudHJ5IGRhdGFzZXQKVFBBLnJhd3NlcS5jb3VudHJpZXMucCA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcz1UUEEubWV0YTIuMSRTYW1wbGVfTmFtZSwgQ291bnRyeT1UUEEubWV0YTIuMSRHZW9fQ291bnRyeSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgojIFByZXBhcmUgY29udGluZW50IGRhdGFzZXQKVFBBLnJhd3NlcS5jb250aW5lbnRzLnAgPC0gZGF0YS5mcmFtZShyb3cubmFtZXM9VFBBLm1ldGEyLjEkU2FtcGxlX05hbWUsIENvbnRpbmVudD1UUEEubWV0YTIuMSRDb250aW5lbnQsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKIyBQcmVwYXJlIFVLIGRhdGEgc3RyaXAKVFBBLnJhd3NlcS5VSy5wIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBFbmdsYW5kPVRQQS5tZXRhMi4xJGlzLlVLLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKVFBBLnJhd3NlcS5VSy5wW1RQQS5yYXdzZXEuVUsucCRFbmdsYW5kPT0iVUsiLF0gPC0gIkVuZ2xhbmQiCgojIFByZXBhcmUgUEhFIGRhdGEgc3RyaXAKVFBBLnJhd3NlcS5QSEUucCA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcz1UUEEubWV0YTIuMSRTYW1wbGVfTmFtZSwgUEhFPVRQQS5tZXRhMi4xJGlzLlBIRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgojIFByZXBhcmUgTWFqb3IgbGluZWFnZSBkYXRhc2V0ClRQQS5yYXdzZXEuTGluZWFnZS5wIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBMaW5lYWdlPVRQQS5tZXRhMi4xJFRQQV9MaW5lYWdlLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKCiMgUHJlcGFyZSBzdWJsaW5lYWdlIGxpbmVhZ2UgZGF0YXNldApUUEEucmF3c2VxLnN1YkxpbmVhZ2UucCA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcz1UUEEubWV0YTIuMSRTYW1wbGVfTmFtZSwgU3VibGluZWFnZT1UUEEubWV0YTIuMSRUUEEucGluZWNvbmUuc3VibGluZWFnZSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgoKIyBQcmVwYXJlIFllYXIgZGF0YXNldCAoYWxsIHNhbXBsZXMpClRQQS5yYXdzZXEuYWxsLlllYXJzLnAgPC0gZGF0YS5mcmFtZShyb3cubmFtZXM9VFBBLm1ldGEyLjEkU2FtcGxlX05hbWUsIFllYXI9VFBBLm1ldGEyLjEkU2FtcGxlX1llYXIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKCmZsb29yXzV5ZWFycyAgPC0gZnVuY3Rpb24odmFsdWUpeyByZXR1cm4odmFsdWUgLSB2YWx1ZSAlJSA1KSB9ClRQQS5tZXRhMi4xJFNhbXBsZV81eWVhci53aW5kb3cgPC0gcGFzdGUwKGZsb29yXzV5ZWFycyhhcy5udW1lcmljKFRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyKSksIi0iLGZsb29yXzV5ZWFycyhhcy5udW1lcmljKFRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyKSkrNSkKIyBTb21lIHNhbXBsZXMgaGF2ZSB1bmNlcnRhaW4gZGF0ZXMgKHVwIHRvIDIwLTMwIHllYXJzIHVuY2VydGFpbnR5KSwgYnV0IGZvciB0aGUgcHVycG9zZXMgb2YgdGhlc2UgcGxvdHRpbmcgY2F0ZWdvcmllcyB3ZSdsbCB1c2UgdGhlIGNlbnRyZXBvaW50IHllYXIKVFBBLm1ldGEyLjEkU2FtcGxlXzV5ZWFyLndpbmRvdyA8LSBzYXBwbHkoMTpucm93KFRQQS5tZXRhMi4xKSwgZnVuY3Rpb24oeCkgaWZlbHNlKFRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyW3hdPT0iLSIsTkEsIGlmZWxzZShpcy5uYShUUEEubWV0YTIuMSRTYW1wbGVfNXllYXIud2luZG93W3hdKSxOQSwgaWZlbHNlKFRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyW3hdPT0iMTk1MC0xOTgwIiwiMTk2NS0xOTcwIixpZmVsc2UoVFBBLm1ldGEyLjEkU2FtcGxlX1llYXJbeF09PSIxOTYwLTE5ODAiLCIxOTY1LTE5NzAiICxpZmVsc2UoVFBBLm1ldGEyLjEkU2FtcGxlX1llYXJbeF09PSIxOTgwLTE5OTkiLCIxOTg1LTE5OTAiLFRQQS5tZXRhMi4xJFNhbXBsZV81eWVhci53aW5kb3dbeF0pKSkpKSkKCgpUUEEubWV0YTIuMSRTYW1wbGVfeWVhci4xOTkwLmN1dHRvZmYgPC0gaWZlbHNlKFRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyPjE5OTAsVFBBLm1ldGEyLjEkU2FtcGxlX1llYXIsIjwxOTkwIikKClRQQS5tZXRhMi4xJFNhbXBsZV95ZWFyLjE5OTkuY3V0dG9mZiA8LSBpZmVsc2UoVFBBLm1ldGEyLjEkU2FtcGxlX1llYXI+MTk5OSxUUEEubWV0YTIuMSRTYW1wbGVfWWVhciwiPDE5OTkiKQpUUEEucmF3c2VxLnllYXIuY3V0dG9mZi5wIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBTYW1wbGUuWWVhcj1UUEEubWV0YTIuMSRTYW1wbGVfeWVhci4xOTk5LmN1dHRvZmYsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKYGBgClwKXAojIEJyaW5nIGluIFBIRSBtZXRhZGF0YQpgYGB7cn0KUEhFLm1ldGFkYXRhLmxpbmtlZCA8LSByZWFkeGw6OnJlYWRfZXhjZWwoUEhFLm1ldGFkYXRhLmxpbmtlZC5maWxlKQpgYGAKXApEbyBzb21lIGNsZWFudXAgYW5kIGZhY3RvcmluZyBvZiB2YXJpYWJsZXMKYGBge3J9CgpQSEUubWV0YWRhdGEubGlua2VkJGFnZV9ncm91cCA8LSBmYWN0b3IoUEhFLm1ldGFkYXRhLmxpbmtlZCRhZ2VfZ3JvdXAsIGxldmVscz1yZXYoYygiMTYtMjQiLCIyNS0zNCIsIjM1LTQ0IiwiNDUrIiwiVW5rbm93biIpKSkKClBIRS5tZXRhZGF0YS5saW5rZWQkbG9uZG9uIDwtIGZhY3RvcihQSEUubWV0YWRhdGEubGlua2VkJGxvbmRvbixsZXZlbHM9cmV2KGMoIlllcyIsIk5vIiwiVW5rbm93biIpKSkKUEhFLm1ldGFkYXRhLmxpbmtlZCR1a2Jvcm4gPC0gZmFjdG9yKFBIRS5tZXRhZGF0YS5saW5rZWQkdWtib3JuLGxldmVscz1yZXYoYygiWWVzIiwiTm8iLCJVbmtub3duIikpKQpQSEUubWV0YWRhdGEubGlua2VkJGhpdnBvcyA8LSBmYWN0b3IoUEhFLm1ldGFkYXRhLmxpbmtlZCRoaXZwb3MsIGxldmVscz1yZXYoYygiWWVzIiwiTm8iLCJVbmtub3duIikpKQoKIyBuZWVkIHRvIHVwZGF0ZSB0ZXJtaW5vbG9neSBvZiAnTVNNJyB0byAnR0JNU00nClBIRS5tZXRhZGF0YS5saW5rZWRbUEhFLm1ldGFkYXRhLmxpbmtlZCRnZW5kZXJfb3JpZW50YXRpb249PSJNU00iLCJnZW5kZXJfb3JpZW50YXRpb24iXSA8LSAiR0JNU00iClBIRS5tZXRhZGF0YS5saW5rZWQkZ2VuZGVyX29yaWVudGF0aW9uIDwtIGZhY3RvcihQSEUubWV0YWRhdGEubGlua2VkJGdlbmRlcl9vcmllbnRhdGlvbiwgbGV2ZWxzPXJldihjKCJNU1ciLCJHQk1TTSIsIldTTSIsIk1Vbmtub3duIiwiVW5rbm93biIpKSkKClBIRS5tZXRhZGF0YS5saW5rZWQkcGhlX2NlbnRyZSA8LSBmYWN0b3IoUEhFLm1ldGFkYXRhLmxpbmtlZCRwaGVfY2VudHJlLCBsZXZlbHM9cmV2KGMoIkVhc3QgTWlkbGFuZHMiLCAiRWFzdCBvZiBFbmdsYW5kIiwgIkxvbmRvbiIsICJOb3J0aCBFYXN0IiwgIk5vcnRoIFdlc3QiLCAiU291dGggRWFzdCIsICJTb3V0aCBXZXN0IiwgIldlc3QgTWlkbGFuZHMiLCAiWW9ya3NoaXJlIGFuZCBIdW1iZXIiLCAiVUsgKG5vdCBFbmdsYW5kKSIsICJOb3QgS25vd24iKSkpCgpQSEUubWV0YWRhdGEubGlua2VkJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlIDwtICBmYWN0b3IoUEhFLm1ldGFkYXRhLmxpbmtlZCRUUEEucGluZWNvbmUuc3VibGluZWFnZSwgbGV2ZWxzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlKQoKYGBgClwKXAojIyMgRXh0cmFjdCBpbmZvcm1hdGlvbiBhYm91dCBkdXBsaWNhdGVzCmBgYHtyfQpQSEUubWV0YWRhdGEuZHVwbGljYXRlcyA8LSBQSEUubWV0YWRhdGEubGlua2VkWyFpcy5uYShQSEUubWV0YWRhdGEubGlua2VkJGR1cF9mbGFnKSxdClBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzIDwtIFBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzWyFpcy5uYShQSEUubWV0YWRhdGEuZHVwbGljYXRlcyRTYW1wbGVfTmFtZSksXQoKClBIRS5wYXRpZW50Lm1hdGNoZXMgPC0gZGF0YS5mcmFtZSgKICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkdXBfZmxhZyA9IGMoIjFBIiwiMUIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMkEiLCIyQiIsIjNBIiwiM0IiLCI0QSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI0QiIsIjVBIiwiNUIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkdXBfUGF0aWVudCA9IGMoIlBhdGllbnQgMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQYXRpZW50IDEiLCJQYXRpZW50IDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUGF0aWVudCAyIiwiUGF0aWVudCAzIiwiUGF0aWVudCAzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBhdGllbnQgNCIsIlBhdGllbnQgNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQYXRpZW50IDUiLCJQYXRpZW50IDUiKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGR1cF9QYXRpZW50X1NhbXBsZSA9IGMoInNhbXBsZSAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNhbXBsZSAyIiwic2FtcGxlIDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2FtcGxlIDIiLCJzYW1wbGUgMSIsInNhbXBsZSAyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNhbXBsZSAxIiwic2FtcGxlIDIiLCJzYW1wbGUgMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzYW1wbGUgMiIpCiAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgIAoKUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMgPC0gbGVmdF9qb2luKFBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzLCBQSEUucGF0aWVudC5tYXRjaGVzLCBieT0iZHVwX2ZsYWciKQoKUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMKYGBgCgpEdXBsaWNhdGUgU2FtcGxlcyBtaXNzaW5nIG1ldGFkYXRhIGFyZSBhbGwgJ25ldyBkdXBsaWNhdGVzJyBhbmQgd2VyZSBleGNsdWRlZCBkdWUgdG8gbG93IG1hcHBpbmcgY292ZXJhZ2UgKGFsbCBjaGVja2VkKS4KXApTYW1wbGVzIGxhYmVsbGVkICdaQScgYW5kICdYQicgaGFkIGR1cGxpY2F0ZXMgaW4gdGhlIG9yaWdpbmFsIGRhdGFzZXQsIGJ1dCB0aGUgcmVjaXByb2NhbCBwYWlycyB3ZXJlIGV4Y2x1ZGVkIGR1ZSB0byBxdWFsaXR5IGlzdWVzLgpcCkF2YWlsYWJsZSBwYWlycyAtIFBhdGllbnQgMywgUGF0aWVudCA0CgpgYGB7cn0KUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMucGFpcmVkIDwtIFBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzW1BIRS5tZXRhZGF0YS5kdXBsaWNhdGVzJGR1cF9QYXRpZW50ICVpbiUgYygiUGF0aWVudCAzIiwiUGF0aWVudCA0IiksXQpQSEUubWV0YWRhdGEuZHVwbGljYXRlcy5wYWlyZWRbb3JkZXIoUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMucGFpcmVkJGR1cF9QYXRpZW50LCBQSEUubWV0YWRhdGEuZHVwbGljYXRlcy5wYWlyZWQkeWVhcixQSEUubWV0YWRhdGEuZHVwbGljYXRlcy5wYWlyZWQkbW9udGgpLGMoIlNhbXBsZV9OYW1lIiwiZHVwX1BhdGllbnQiLCAibW9udGguZml4IiwgInllYXIiKV0KYGBgClwKVGhlc2Ugd2lsbCBiZSByZXZpc2l0ZWQgbGF0ZXIgaW4gdGhlIGFuYWx5c2lzLiAKXApQYXRpZW50IDQKSElWLXZlIE1TTSAoNDUrKSwgVUsgYm9ybiwgUEhFIHJlZ2lvbiBECjIgc2FtcGxlcywgY29sbGVjdGVkIGluIHRoZSBzYW1lIG1vbnRoIGFuZCB5ZWFyCkJvdGggc2FtcGxlcyBhcmUgc3VibGluZWFnZSAxLCBhbmQgaWRlbnRpY2FsICgwIHB3U05QcykKTGlrZWx5IHRoZSBzYW1lIGluZmVjdGlvbiAoZGVwZW5kaW5nIG9uIGRhdGVzLCB0cmVhdG1lbnQsIGV0YyksIGJ1dCBjYW7igJl0IHJ1bGUgb3V0IHJlaW5mZWN0aW9uIHdpdGggc2FtZSBzdHJhaW4uClwKUGF0aWVudCAzCkhJVi12ZSBNU00gKDM1LTQ0KSwgbm90IFVLIGJvcm4sIGJhc2VkIGluIExvbmRvbiAoQykKMiBzYW1wbGVzLCBjb2xsZWN0ZWQgOSBtb250aHMgYXBhcnQKQm90aCBzYW1wbGVzIGFyZSBzdWJsaW5lYWdlIDEsIGJ1dCBoYXZlIDcgcGFpcndpc2UgU05QcyBiZXR3ZWVuIHRoZW0gKGxvYWRzISkKUmVpbmZlY3Rpb24g4oCTIHByb2JhYmx5IGZyb20gYSBkaWZmZXJlbnQgdHJhbnNtaXNzaW9uIG5ldHdvcmsKXApcCkhvd2V2ZXIsIGJhc2VkIG9uIHRoZSBzYW1wbGUgZGF0ZXMsIGFzIHdlbGwgYXMgdGhlIG91dGNvbWUgb2YgdGhlIGRvd25zdHJlYW0gZ2VuZXRpYyBhbmFseXNpcywgd2UgY2FuIHNlZSB0aGF0IFBhdGllbnQgMyBoYXMgZHVwbGljYXRlIGluZmVjdGlvbiBldmVudHMgKGRpZmZlcmVudCBkYXRlcywgMTAgbW9udGhzIGFwYXJ0KSBhbmQgdGhlIGdlbm9tZXMgYXJlIGRpc3RpbmN0ICg3IFNOUHMgYXBhcnQpLCB3aGVyZWFzIFBhdGllbnQgNCBzYW1wbGVzIHdlcmUgY29sbGVjdGVkIGluIHRoZSBzYW1lIG1vbnRoIGFuZCB5ZWFyIChpLmUuIGFyZSBsaWtlbHkgZHVwbGljYXRlcyBmcm9tIHRoZSBzYW1lIGluZmVjdGlvbikgYW5kIGhhcyBpZGVudGljYWwgZ2Vub21lcy4KXApGb3IgZG93bnN0cmVhbSBhbmFseXNpcyBwdXJwb3Nlcywgd2Ugd2lsbCByZXRhaW4gYm90aCBzYW1wbGVzIGZvciBQYXRpZW50IDMgKGRpc2NyZXRlIGluZmVjdGlvbnMpLCBidXQgZXhjbHVkZSBvbmUgc2FtcGxlIGZyb20gUGF0aWVudCA0IChkdXBsaWNhdGUgaW5mZWN0aW9uIHNhbXBsZXMpIC0gJ1BIRTE1MDEyNkEnIGhhcyBtdWNoIGJldHRlciBnZW5vbWUgY292ZXJhZ2UsIHNvIGV4Y2x1ZGUgJ1BIRTE1MDEyNUEnClwKXAojIyMgRnVydGhlciBFeGNsdXNpb25zIFwKUEhFMTMwMDU2QSAtIGR1cGxpY2F0ZSBvZiBQSEUxMzAwNTdCIChhbHJlYWR5IHJlbW92ZWQsIHNvIG5vdCByZWxldmFudCkgLSBkb24ndCBleGNsdWRlIQpQSEUxNzA0MDJBIC0gcXVhbGl0eSBjb250cm9sIHNhbXBsZQpQSEUxNzAzNzhBIC0gcXVhbGl0eSBjb250cm9sIHNhbXBsZQoKXApFeGNsdWRlIGR1cGxpY2F0ZSBzZXF1ZW5jZXMKYGBge3J9CmR1cGxpY2F0ZS5leGNsdXNpb24ubGlzdCA8LSBjKCJQSEUxNTAxMjVBIiwiUEhFMTcwNDAyQSIsIlBIRTE3MDM3OEEiKQpQSEUubWV0YWRhdGEubGlua2VkIDwtIFBIRS5tZXRhZGF0YS5saW5rZWRbUEhFLm1ldGFkYXRhLmxpbmtlZCRTYW1wbGVfTmFtZSAlbm90aW4lIGR1cGxpY2F0ZS5leGNsdXNpb24ubGlzdCxdCmBgYApcCgojIyMgTW92aW5nIG9uLi4uIFwKCkRlZmluZSBzb21lIGNvbG91ciBzY2hlbWVzCmBgYHtyfQojIGRlZmluZSBzb21lIGNvbG9ycyBmb3IgZWFjaCByZWdpb24KUEhFLnJlZ2lvbi5jb2xzLmJyZXcgPC0gZGF0YS5mcmFtZShVS0hTQS5yZWdpb249YygiTm9ydGggRWFzdCIsICJOb3J0aCBXZXN0IiwgIllvcmtzaGlyZSBhbmQgSHVtYmVyIiwgIkVhc3QgTWlkbGFuZHMiLCAiV2VzdCBNaWRsYW5kcyIsICJFYXN0IG9mIEVuZ2xhbmQiLCAiTG9uZG9uIiwgIlNvdXRoIEVhc3QiLCJTb3V0aCBXZXN0IiwiVUsgKG5vdCBFbmdsYW5kKSIsICJOb3QgS25vd24iKSwgc3RyaW5nc0FzRmFjdG9ycz1GKQpQSEUucmVnaW9uLmNvbHMuYnJldyRyZWdpb24uY29sIDwtIGMoIiNBNkNFRTMiLCIjMUY3OEI0IiwiI0NBQjJENiIsIiMzM0EwMkMiLCIjQjJERjhBIiwiI0ZGN0YwMCIsIiNFMzFBMUMiLCIjRkI5QTk5IiwiI0Q0QkIwMiIsImdyZXk3NSIsImdyZXkyNSIpCgojIEhJViBjb2xvciBzY2hlbWUKUEhFLmhpdi5jb2xzIDwtIGRhdGEuZnJhbWUoaGl2cG9zPXJldihzb3J0KHVuaXF1ZShQSEUubWV0YWRhdGEubGlua2VkJGhpdnBvcykpKSwgc3RyaW5nc0FzRmFjdG9ycz1GKQpQSEUuaGl2LmNvbHMkaGl2LmNvbHMgPC0gYygiIzFmNzhiNCIsIiNiMmRmOGEiLCJncmV5NzUiKQoKIyBPcmllbnRhdGlvbiBjb2xvdXIgc2NoZW1lClBIRS5vcmllbnRhdGlvbi5jb2xzIDwtIGRhdGEuZnJhbWUob3JpZW50YXRpb249cmV2KHNvcnQodW5pcXVlKFBIRS5tZXRhZGF0YS5saW5rZWQkZ2VuZGVyX29yaWVudGF0aW9uKSkpLCBzdHJpbmdzQXNGYWN0b3JzPUYpClBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uIDwtIGZhY3RvcihQSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbiwgbGV2ZWxzPXJldihzb3J0KHVuaXF1ZShQSEUubWV0YWRhdGEubGlua2VkJGdlbmRlcl9vcmllbnRhdGlvbikpKSwgbGFiZWxzPWMoIk1TVyIsIkdCTVNNIiwiV1NNIiwiTVVua25vd24iLCJVbmtub3duIikpClBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uLmNvbHMgPC0gYygiIzFmNzhiNCIsIiNiMmRmOGEiLCIjZmI5YTk5IiwiI2E2Y2VlMyIsImdyZXk3NSIpCgojIFVLIGJvcm4gY29sb3VyIHNjaGVtZQpQSEUudWtib3JuLmNvbHMgPC0gZGF0YS5mcmFtZSh1a2Jvcm49cmV2KHNvcnQodW5pcXVlKFBIRS5tZXRhZGF0YS5saW5rZWQkdWtib3JuKSkpLHVrYm9ybi5jb2xzPWMoIiMxZjc4YjQiLCIjYjJkZjhhIiwiZ3JleTc1Iiksc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgojIExvbmRvbiBiYXNlZCBjb2xvdXIgc2NoZW1lClBIRS5sb25kb24uY29scyA8LSBkYXRhLmZyYW1lKGxvbmRvbj1yZXYoc29ydCh1bmlxdWUoUEhFLm1ldGFkYXRhLmxpbmtlZCRsb25kb24pKSksbG9uZG9uLmNvbHM9YygiIzFmNzhiNCIsIiNiMmRmOGEiLCJncmV5NzUiKSxzdHJpbmdzQXNGYWN0b3JzID0gRikKCgojIEFnZSBncm91cCBjb2xvdXIgc2NoZW1lClBIRS5BZ2UuY29scyA8LSBkYXRhLmZyYW1lKGFnZV9ncm91cD1yZXYoc29ydCh1bmlxdWUoUEhFLm1ldGFkYXRhLmxpbmtlZCRhZ2VfZ3JvdXApKSksc3RyaW5nc0FzRmFjdG9ycyA9IFQpClBIRS5BZ2UuY29scyRhZ2VfZ3JvdXAuY29scyA8LSBjKGJyZXdlci5wYWwobj00LCJZbEduQnUiKSwiZ3JleTc1IikKCiMgU2FtcGxlIERhdGUgY29sb3VyIHNjaGVtZQpQSEUueWVhci5jb2xzIDwtIGRhdGEuZnJhbWUoeWVhcj0oc29ydCh1bmlxdWUoUEhFLm1ldGFkYXRhLmxpbmtlZCR5ZWFyKSkpLHN0cmluZ3NBc0ZhY3RvcnMgPSBUKQpQSEUueWVhci5jb2xzJHllYXIuY29scyA8LSBicmV3ZXIucGFsKG49NywiWWxPclJkIikKCiMgU2FtcGxlIERhdGUgKGFsbCBnbG9iYWwgZGF0YSwgYnV0IHdpdGggMTk5MCBjdXR0b2ZmKQpUUEEueWVhci5jdXR0b2ZmLmNvbHMgPC0gZGF0YS5mcmFtZShkYXRlLmN1dHRvZmY9YygiPDE5OTkiLDE5OTk6MjAxOSksIGRhdGUuY3V0dG9mZi5jb2w9YygiI0YyRjJGMiIsY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKDcsICJZbE9yUmQiKSkobGVuZ3RoKDE5OTk6MjAxOSkpKSkKCgpgYGAKXApcCiMjIyMjCiMjIEZpcnN0IGRlc2NyaWJlIHRoZSBzZXF1ZW5jZWQgcG9wdWxhdGlvbiBhcyBhIHdob2xlClwKU2V0IG9yZGVyIG9mIFBIRSByZWdpb25zCmBgYHtyfQpQSEUubWV0YWRhdGEubGlua2VkJHBoZV9jZW50cmUgPC0gZmFjdG9yKFBIRS5tZXRhZGF0YS5saW5rZWQkcGhlX2NlbnRyZSwgbGV2ZWxzPXJldihQSEUucmVnaW9uLmNvbHMuYnJldyRVS0hTQS5yZWdpb24pKQpgYGAKXApHZW5lcmF0ZSBzb21lIGJhc2ljIHN0YXRpc3RpY3MgYWJvdXQgZ2VvZ3JhcGhpY2FsIFBIRSByZWdpb25zIChhbm9ueW1pc2VkKQpgYGB7cn0KUEhFLmNvdW50LmFsbCA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LnBlci5yZWdpb249bigpKQoKUEhFLmNvdW50LnllYXJzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieSh5ZWFyKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LnBlci55ZWFyPW4oKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGRwbHlyOjptdXRhdGUocGVyYy5wZXIueWVhcj0oY291bnQucGVyLnllYXIvc3VtKGNvdW50LnBlci55ZWFyKSkqMTAwKQoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IEhJViBzdGF0dXMKUEhFLkhJVi5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGhpdnBvcykgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwucmVnaW9uPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwucmVnaW9uKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKGhpdnBvcyksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGN1bV9mcmFjdCA9IGN1bXN1bShmcmFjdGlvbikpICU+JQogIGRwbHlyOjptdXRhdGUoY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpICU+JQogIGRwbHlyOjptdXRhdGUoSElWLnBlcmM9KENvdW50L3N1bShDb3VudCkqMTAwKSkKICAKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IGdlbmRlciBvcmllbnRhdGlvbgpQSEUub3JpZW50YXRpb24uY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShnZW5kZXJfb3JpZW50YXRpb24pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKGdlbmRlcl9vcmllbnRhdGlvbiksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKSAlPiUgCiAgZHBseXI6Om11dGF0ZShvcmllbnRhdGlvbi5wZXJjPShDb3VudC9zdW0oQ291bnQpKjEwMCkpCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgVUsgYm9ybiAodmFndWUgY2F0ZWdvcnkgdGhhdCdzIHVuZm9ydHVuYXRlbHkgb25seSBtYXJnaW5hbGx5IGhlbHBmdWwpClBIRS5VS2Jvcm4uY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieSh1a2Jvcm4pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKHVrYm9ybiksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKSAlPiUgCiAgZHBseXI6Om11dGF0ZShVS2Jvcm4ucGVyYz0oQ291bnQvc3VtKENvdW50KSoxMDApKQogIAojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgTG9uZG9uIGJhc2VkClBIRS5Mb25kb24uY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShsb25kb24pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKGxvbmRvbiksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKSAlPiUKICBkcGx5cjo6bXV0YXRlKExvbmRvbi5wZXJjPShDb3VudC9zdW0oQ291bnQpKjEwMCkpCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgQWdlIGdyb3VwClBIRS5BZ2UuY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShhZ2VfZ3JvdXApICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKGFnZV9ncm91cCksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKSAlPiUKICBkcGx5cjo6bXV0YXRlKEFnZS5wZXJjPShDb3VudC9zdW0oQ291bnQpKjEwMCkpCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgTGluZWFnZSBncm91cApQSEUuTGluZWFnZS5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFRQQV9MaW5lYWdlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhUUEFfTGluZWFnZSksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKSAlPiUKICBkcGx5cjo6bXV0YXRlKExpbmVhZ2UucGVyYz0oQ291bnQvc3VtKENvdW50KSoxMDApKQoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IHN1YmxpbmVhZ2UgZ3JvdXAKUEhFLnN1YmxpbmVhZ2UuY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwucmVnaW9uPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkgJT4lCiAgZHBseXI6Om11dGF0ZShTdWJsaW5lYWdlLnBlcmM9KENvdW50L3N1bShDb3VudCkqMTAwKSkKYGBgClwKTWFrZSBzb21lIHBsb3RzCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OH0KIyBNYWtlIGhiYXIgcGxvdCBvZiBzYW1wbGUgY291bnRzIGJ5IHJlZ2lvbgpwLmFsbC5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLmNvdW50LmFsbCwgYWVzKHg9Y291bnQucGVyLnJlZ2lvbix5PSIiKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJzdGFjayIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J25vbmUnKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSJncmV5MzAiKSArIAogIGdlb21fdGV4dChkYXRhPVBIRS5jb3VudC5hbGwsIGFlcygoY291bnQucGVyLnJlZ2lvbisxMiksICIiLGxhYmVsPWNvdW50LnBlci5yZWdpb24pLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikgKwogIGxhYnMoeT0iQWxsIiwgeD0iU2FtcGxlIENvdW50IikgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltPWMoMCwyNjApKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9NCkpIAojcC5hbGwuaGJhcnBsb3QKCiMgbWFrZSB0ZW1wb3JhbCBidWJibGVwbG90IG9mIGNvdW50cyBieSByZWdpb24KcC5hbGwueWVhci5idWJibGVwbG90IDwtIGdncGxvdChQSEUuY291bnQueWVhcnMsIGFlcyhhcy5udW1lcmljKHllYXIpLCB5PSJBbGwiKSkgKwogIGdlb21fcG9pbnQoYWxwaGE9MC42NSwgYWVzKHNpemU9Y291bnQucGVyLnllYXIpKSArIAogIGdlb21fbGluZShhbHBoYT0wLjI1KSArCiAgZ3VpZGVzKGNvbG91cj0nbm9uZScpICsKICBzY2FsZV9zaXplX2FyZWEobWF4X3NpemUgPSA3LGJyZWFrcz1jKDEsNSwxMCwyNSw1MCkpICsgCiAgZ3VpZGVzKHNpemU9Z3VpZGVfbGVnZW5kKG5yb3c9MikpICsKICB0aGVtZV9saWdodCgpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9ImdyZXkzMCIpICsgCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdub25lJykgKwogIGxhYnMoeT0iIiwgeD0iU2FtcGxlIFllYXIiLCBzaXplPSJDb3VudCIpIAojcC5hbGwueWVhci5idWJibGVwbG90CgojIE1ha2UgcHJvcG9ydGlvbmFsIGhiYXIgcGxvdCBvZiBISVYgc3RhdHVzCnAuYWxsLmhpdi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLkhJVi5jb3VudHMsIGFlcyhDb3VudCx5PSIiLGZpbGw9aGl2cG9zKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nbm9uZScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJISVYgK3ZlIix2YWx1ZXM9UEhFLmhpdi5jb2xzJGhpdi5jb2xzLCBicmVha3M9UEhFLmhpdi5jb2xzJGhpdnBvcykgKwogIGxhYnMoeT0iQWxsIiwgeD0iSElWICt2ZSIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz0zKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5ISVYuY291bnRzLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgeT0iIixsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKSArCiAgTlVMTAojcC5hbGwuaGl2LmhiYXJwbG90CgpwLmFsbC5vcmllbnRhdGlvbi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLm9yaWVudGF0aW9uLmNvdW50cywgYWVzKENvdW50LHk9IiIsZmlsbD1nZW5kZXJfb3JpZW50YXRpb24pKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdub25lJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9Ik9yaWVudGF0aW9uIix2YWx1ZXM9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24uY29scywgYnJlYWtzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uKSArCiAgbGFicyh5PSJBbGwiLCB4PSJPcmllbnRhdGlvbiIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz0zKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5vcmllbnRhdGlvbi5jb3VudHMsIGFlcyhjdW1fZnJhY3QubWlkLCB5PSIiLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpCiNwLmFsbC5vcmllbnRhdGlvbi5oYmFycGxvdAoKcC5hbGwudWtib3JuLmhiYXJwbG90IDwtIGdncGxvdChQSEUuVUtib3JuLmNvdW50cywgYWVzKENvdW50LHk9IiIsZmlsbD11a2Jvcm4pKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdub25lJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlVLXG5Cb3JuIix2YWx1ZXM9UEhFLnVrYm9ybi5jb2xzJHVrYm9ybi5jb2xzLCBicmVha3M9UEhFLnVrYm9ybi5jb2xzJHVrYm9ybikgKwogIGxhYnMoeT0iQWxsIiwgeD0iVUsgQm9ybiIpICsKICAjZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9MykpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuVUtib3JuLmNvdW50cywgYWVzKGN1bV9mcmFjdC5taWQsIHk9IiIsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3AuYWxsLnVrYm9ybi5oYmFycGxvdAoKcC5hbGwuTG9uZG9uLmhiYXJwbG90IDwtIGdncGxvdChQSEUuTG9uZG9uLmNvdW50cywgYWVzKENvdW50LHk9IiIsZmlsbD1sb25kb24pKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdub25lJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkxvbmRvbiIsdmFsdWVzPVBIRS5sb25kb24uY29scyRsb25kb24uY29scywgYnJlYWtzPVBIRS5sb25kb24uY29scyRsb25kb24pICsKICBsYWJzKHk9IkFsbCIsIHg9IkxvbmRvbiIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz0zKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5Mb25kb24uY291bnRzLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgeT0iIixsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5hbGwuTG9uZG9uLmhiYXJwbG90CgpwLmFsbC5BZ2UuaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5BZ2UuY291bnRzLCBhZXMoQ291bnQseT0iIixmaWxsPWFnZV9ncm91cCkpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J25vbmUnKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iQWdlXG5Hcm91cCIsdmFsdWVzPVBIRS5BZ2UuY29scyRhZ2VfZ3JvdXAuY29scywgYnJlYWtzPVBIRS5BZ2UuY29scyRhZ2VfZ3JvdXApICsKICBsYWJzKHk9IkFsbCIsIHg9IkFnZSBHcm91cCIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz0zKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5BZ2UuY291bnRzLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgeT0iIixsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5hbGwuQWdlLmhiYXJwbG90CmBgYApcClBsb3QgY29tYmluZWQgcGxvdCBmb3IgJ2FsbCBzYW1wbGVzJwpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEwfQpQSEUuYWxsLmNvbWJpcGxvdC4xIDwtIHBsb3RfZ3JpZChwLmFsbC55ZWFyLmJ1YmJsZXBsb3QsIHAuYWxsLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5hbGwub3JpZW50YXRpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLmFsbC5oaXYuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLmFsbC5BZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBucm93PTEsIGFsaWduPSJoIiwgcmVsX3dpZHRocz1jKDQsMiwyLDIsMiksIHNjYWxlPTAuOSkKClBIRS5hbGwuY29tYmlwbG90LjEKYGBgClwKXApOZXh0IGp1c3QgZGVzY3JpYmUgcG9wdWxhdGlvbiBkaXN0cmlidXRpb25zIGJ5IFBIRSByZWdpb24KYGBge3J9CiMgZ2VuZXJhdGUgc29tZSBiYXNpYyBzdGF0aXN0aWNzIGFib3V0IGdlb2dyYXBoaWNhbCBQSEUgcmVnaW9ucyAoYW5vbnltaXNlZCkKUEhFLmdlby5jb3VudCA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkocGhlX2NlbnRyZSkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudC5wZXIucmVnaW9uPW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5jb3VudD1zdW0oY291bnQucGVyLnJlZ2lvbiksZnJhY3Rpb249Y291bnQucGVyLnJlZ2lvbi90b3RhbC5jb3VudCkKClBIRS5nZW8uY291bnQueWVhcnMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHBoZV9jZW50cmUseWVhcikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudC5wZXIucmVnaW9uLnllYXI9bigpKQoKUEhFLmdlby5jb3VudC55ZWFycy5saW5lYWdlIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShwaGVfY2VudHJlLHllYXIsVFBBX0xpbmVhZ2UpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQucGVyLnJlZ2lvbi55ZWFyPW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5jb3VudC55ZWFyPXN1bShjb3VudC5wZXIucmVnaW9uLnllYXIpKSAlPiUKICBkcGx5cjo6dW5ncm91cCgpICU+JQogIHRpZHlyOjpwaXZvdF93aWRlcihuYW1lc19mcm9tPVRQQV9MaW5lYWdlLCB2YWx1ZXNfZnJvbSA9IGNvdW50LnBlci5yZWdpb24ueWVhcikKUEhFLmdlby5jb3VudC55ZWFycy5saW5lYWdlW2lzLm5hKFBIRS5nZW8uY291bnQueWVhcnMubGluZWFnZSldIDwtIDAKUEhFLmdlby5jb3VudC55ZWFycy5saW5lYWdlJHllYXIgPC0gYXMubnVtZXJpYyhQSEUuZ2VvLmNvdW50LnllYXJzLmxpbmVhZ2UkeWVhcikKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBISVYgc3RhdHVzClBIRS5nZW8uSElWLmNvdW50cyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkocGhlX2NlbnRyZSxoaXZwb3MpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQucGVyLnJlZ2lvbi5oaXY9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oY291bnQucGVyLnJlZ2lvbi5oaXYpKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPWNvdW50LnBlci5yZWdpb24uaGl2L3RvdGFsLnJlZ2lvbikgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhoaXZwb3MpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShjdW1fZnJhY3QgPSBjdW1zdW0oZnJhY3Rpb24pKSAlPiUKICBkcGx5cjo6bXV0YXRlKGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKQoKIyBEb3VibGUgQ2hlY2sgSElWIHN0YXR1cyBkYXRhIGZvciBub24tUEhFIGRhdGFzZXQgLSBjb25maXJtZWQgbm8gSElWK3ZlcyBmcm9tIG5vbi1NU00uIApQSEUuc291cmNlbGFiLkhJVi5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGlzLlBIRSwgZ2VuZGVyX29yaWVudGF0aW9uLCBoaXZwb3MpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQucGVyLm9yaWVudGF0aW9uLmhpdj1uKCkpICMlPiUKICAjZHBseXI6OmZpbHRlcihpcy5QSEUhPSJQSEUiKQoKIyBHZXQgdG90YWwgcG9wdWxhdGlvbiBzdGF0cyBmb3IgSElWClBIRS5hbGwuSElWLmNvdW50cyA8LSAgUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGhpdnBvcykgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudC5oaXY9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKGNvdW50LnRvdGFsPXN1bShjb3VudC5oaXYpLCBmcmFjdGlvbj1jb3VudC5oaXYvY291bnQudG90YWwpCgogIAojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgZ2VuZGVyIG9yaWVudGF0aW9uClBIRS5vcmllbnRhdGlvbi5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGdlbmRlcl9vcmllbnRhdGlvbikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShvcmllbnRhdGlvbi5jb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUob3JpZW50YXRpb24ucGVyY2VudD0ob3JpZW50YXRpb24uY291bnQvc3VtKG9yaWVudGF0aW9uLmNvdW50KSoxMDApKQoKUEhFLmdlby5vcmllbnRhdGlvbi5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHBoZV9jZW50cmUsZ2VuZGVyX29yaWVudGF0aW9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LnBlci5yZWdpb24ub3JpZW50YXRpb249bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oY291bnQucGVyLnJlZ2lvbi5vcmllbnRhdGlvbikpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoZ2VuZGVyX29yaWVudGF0aW9uKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Y291bnQucGVyLnJlZ2lvbi5vcmllbnRhdGlvbi90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkgJT4lIAogIGRwbHlyOjptdXRhdGUob3JpZW50YXRpb24ucGVyY2VudD0oY291bnQucGVyLnJlZ2lvbi5vcmllbnRhdGlvbi9zdW0oY291bnQucGVyLnJlZ2lvbi5vcmllbnRhdGlvbikqMTAwKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBVSyBib3JuClBIRS5nZW8uVUtib3JuIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShwaGVfY2VudHJlLCB1a2Jvcm4pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKHVrYm9ybiksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKQogIAojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgTG9uZG9uIGJhc2VkClBIRS5nZW8uTG9uZG9uIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShwaGVfY2VudHJlLCBsb25kb24pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKGxvbmRvbiksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKQoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IEFnZSBncm91cApQSEUuZ2VvLkFnZSA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkocGhlX2NlbnRyZSwgYWdlX2dyb3VwKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhhZ2VfZ3JvdXApLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBMaW5lYWdlIGdyb3VwClBIRS5nZW8uTGluZWFnZSA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkocGhlX2NlbnRyZSwgVFBBX0xpbmVhZ2UpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKFRQQV9MaW5lYWdlKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwucmVnaW9uLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgc3VibGluZWFnZSBncm91cApQSEUuZ2VvLnN1YmxpbmVhZ2UgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHBoZV9jZW50cmUsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhUUEEucGluZWNvbmUuc3VibGluZWFnZSksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKQpgYGAKXApNYWtlIHNvbWUgcGxvdHMKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD04fQojIE1ha2UgaGJhciBwbG90IG9mIHNhbXBsZSBjb3VudHMgYnkgcmVnaW9uCnAucmVnaW9uLmhiYXJwbG90IDwtIGdncGxvdChQSEUuZ2VvLmNvdW50LCBhZXMoY291bnQucGVyLnJlZ2lvbixwaGVfY2VudHJlLCBmaWxsPXBoZV9jZW50cmUpKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249InN0YWNrIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlVLSFNBXG5SZWdpb24iLHZhbHVlcz1QSEUucmVnaW9uLmNvbHMuYnJldyRyZWdpb24uY29sLCBicmVha3M9UEhFLnJlZ2lvbi5jb2xzLmJyZXckVUtIU0EucmVnaW9uKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLmdlby5jb3VudCwgYWVzKChjb3VudC5wZXIucmVnaW9uKzEyKSwgcGhlX2NlbnRyZSxsYWJlbD1jb3VudC5wZXIucmVnaW9uKSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICBsYWJzKHk9IlVLSFNBIFJlZ2lvbiIsIHg9IlNhbXBsZSBDb3VudCIpICsKICAjY29vcmRfY2FydGVzaWFuKHhsaW09YygwLDEzMCkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDAsMjYwKSkgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChuY29sPTIpKSAKI3AucmVnaW9uLmhiYXJwbG90CgojIG1ha2UgdGVtcG9yYWwgYnViYmxlcGxvdCBvZiBjb3VudHMgYnkgcmVnaW9uCnAucmVnaW9uLnllYXIuYnViYmxlcGxvdCA8LSBnZ3Bsb3QoUEhFLmdlby5jb3VudC55ZWFycywgYWVzKGFzLm51bWVyaWMoeWVhciksIHBoZV9jZW50cmUsIGNvbG91cj1waGVfY2VudHJlKSkgKwogIGdlb21fcG9pbnQoYWxwaGE9MC42NSwgYWVzKHNpemU9Y291bnQucGVyLnJlZ2lvbi55ZWFyKSkgKyAKICBnZW9tX2xpbmUoYWxwaGE9MC4yNSkgKwogIGd1aWRlcyhjb2xvdXI9J25vbmUnKSArCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gNyxicmVha3M9YygxLDUsMTAsMjUsNTApKSArIAogIGd1aWRlcyhzaXplPWd1aWRlX2xlZ2VuZChucm93PTIsIGRpcmVjdGlvbiA9ICdob3Jpem9udGFsJywgYnlyb3c9VCkpICsKICB0aGVtZV9saWdodCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iVUtIU0FcblJlZ2lvbiIsdmFsdWVzPVBIRS5yZWdpb24uY29scy5icmV3JHJlZ2lvbi5jb2wsIGJyZWFrcz1QSEUucmVnaW9uLmNvbHMuYnJldyRVS0hTQS5yZWdpb24pICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBsYWJzKHk9IlVLSFNBIFJlZ2lvbiIsIHg9IlNhbXBsZSBZZWFyIiwgc2l6ZT0iQ291bnQiKSAKI3AucmVnaW9uLnllYXIuYnViYmxlcGxvdAoKIyBPciBhIGJhcnBsb3Qgb2YgbGluZWFnZSBieSB5ZWFyICYgUEhFIHJlZ2lvbj8KcC5yZWdpb24ueWVhci5idWJibGVwbG90LmJhcnBsb3QuZmFjZXQubGluZWFnZSA8LSBQSEUuZ2VvLmNvdW50LnllYXJzLmxpbmVhZ2UgJT4lIHRpZHlyOjpwaXZvdF9sb25nZXIoYyhTUzE0LCBOaWNob2xzKSwgbmFtZXNfdG89IlRQQV9MaW5lYWdlIiwgdmFsdWVzX3RvPSJDb3VudCIpICU+JQogIGdncGxvdChhZXMoeWVhciwgQ291bnQsIGZpbGw9VFBBX0xpbmVhZ2UpKSArIAogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5Jywgd2lkdGg9MC42KSArIAogIGZhY2V0X2dyaWQocGhlX2NlbnRyZX4uLCBzY2FsZXM9J2ZyZWUnKSArCiAgZ3VpZGVzKHNpemU9Z3VpZGVfbGVnZW5kKG5yb3c9MikpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJUUEFcbkxpbmVhZ2UiLHZhbHVlcz1UUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UuY29sLCBicmVha3M9VFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlKSArCiAgdGhlbWUoc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvcj0nd2hpdGUnLCBmaWxsPSd3aGl0ZScsbGluZXR5cGU9InNvbGlkIiksIHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJncmV5MjUiLCBzaXplPTcsIGFuZ2xlPTApKSAKI3AucmVnaW9uLnllYXIuYnViYmxlcGxvdC5iYXJwbG90LmZhY2V0LmxpbmVhZ2UKCiMgTWFrZSBwcm9wb3J0aW9uYWwgaGJhciBwbG90IG9mIEhJViBzdGF0dXMKcC5yZWdpb24uaGl2LmhiYXJwbG90IDwtIGdncGxvdChQSEUuZ2VvLkhJVi5jb3VudHMsIGFlcyhjb3VudC5wZXIucmVnaW9uLmhpdixwaGVfY2VudHJlLGZpbGw9aGl2cG9zKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkhJViArdmUiLHZhbHVlcz1QSEUuaGl2LmNvbHMkaGl2LmNvbHMsIGJyZWFrcz1QSEUuaGl2LmNvbHMkaGl2cG9zKSArCiAgbGFicyh5PSJVS0hTQSBSZWdpb24iLCB4PSJISVYgK3ZlIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTMpKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLmdlby5ISVYuY291bnRzLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgcGhlX2NlbnRyZSxsYWJlbD1jb3VudC5wZXIucmVnaW9uLmhpdiksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKSArCiAgTlVMTAojcC5yZWdpb24uaGl2LmhiYXJwbG90CgpwLnJlZ2lvbi5vcmllbnRhdGlvbi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLmdlby5vcmllbnRhdGlvbi5jb3VudHMsIGFlcyhjb3VudC5wZXIucmVnaW9uLm9yaWVudGF0aW9uLHBoZV9jZW50cmUsZmlsbD1nZW5kZXJfb3JpZW50YXRpb24pKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iT3JpZW50YXRpb24iLHZhbHVlcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbi5jb2xzLCBicmVha3M9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24pICsKICBsYWJzKHk9IlVLSFNBIFJlZ2lvbiIsIHg9Ik9yaWVudGF0aW9uIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChuY29sPTEpKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLmdlby5vcmllbnRhdGlvbi5jb3VudHMsIGFlcyhjdW1fZnJhY3QubWlkLCBwaGVfY2VudHJlLGxhYmVsPWNvdW50LnBlci5yZWdpb24ub3JpZW50YXRpb24pLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3AucmVnaW9uLm9yaWVudGF0aW9uLmhiYXJwbG90CgpwLnJlZ2lvbi51a2Jvcm4uaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5nZW8uVUtib3JuLCBhZXMoQ291bnQscGhlX2NlbnRyZSxmaWxsPXVrYm9ybikpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJVSyBCb3JuIix2YWx1ZXM9UEhFLnVrYm9ybi5jb2xzJHVrYm9ybi5jb2xzLCBicmVha3M9UEhFLnVrYm9ybi5jb2xzJHVrYm9ybikgKwogIGxhYnMoeT0iVUtIU0EgUmVnaW9uIiwgeD0iVUsgQm9ybiIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz0zKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5nZW8uVUtib3JuLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgcGhlX2NlbnRyZSxsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5yZWdpb24udWtib3JuLmhiYXJwbG90CgpwLnJlZ2lvbi5Mb25kb24uaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5nZW8uTG9uZG9uLCBhZXMoQ291bnQscGhlX2NlbnRyZSxmaWxsPWxvbmRvbikpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJMb25kb24iLHZhbHVlcz1QSEUubG9uZG9uLmNvbHMkbG9uZG9uLmNvbHMsIGJyZWFrcz1QSEUubG9uZG9uLmNvbHMkbG9uZG9uKSArCiAgbGFicyh5PSJVS0hTQSBSZWdpb24iLCB4PSJMb25kb24iKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9MykpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuZ2VvLkxvbmRvbiwgYWVzKGN1bV9mcmFjdC5taWQsIHBoZV9jZW50cmUsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3AucmVnaW9uLkxvbmRvbi5oYmFycGxvdAoKcC5yZWdpb24uQWdlLmhiYXJwbG90IDwtIGdncGxvdChQSEUuZ2VvLkFnZSwgYWVzKENvdW50LHBoZV9jZW50cmUsZmlsbD1hZ2VfZ3JvdXApKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iQWdlXG5Hcm91cCIsdmFsdWVzPVBIRS5BZ2UuY29scyRhZ2VfZ3JvdXAuY29scywgYnJlYWtzPVBIRS5BZ2UuY29scyRhZ2VfZ3JvdXApICsKICBsYWJzKHk9IlVLSFNBIFJlZ2lvbiIsIHg9IkFnZSBHcm91cCIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbD0xKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5nZW8uQWdlLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgcGhlX2NlbnRyZSxsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5yZWdpb24uQWdlLmhiYXJwbG90CmBgYApcCkNvbWJpbmVkIHBsb3QKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMH0KUEhFLnJlZ2lvbi5jb21iaXBsb3QuMSA8LSBwbG90X2dyaWQocC5yZWdpb24ueWVhci5idWJibGVwbG90LCBwLnJlZ2lvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAucmVnaW9uLm9yaWVudGF0aW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5yZWdpb24uaGl2LmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5yZWdpb24uQWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgbnJvdz0xLCBhbGlnbj0iaCIsIHJlbF93aWR0aHM9Yyg0LDIsMiwyLDIpLCBzY2FsZT0wLjkpCgpQSEUucmVnaW9uLmNvbWJpcGxvdC4xCmBgYAoKClwKUmVnaW9ucyBhcyBhIGNvbXBsZXggbXVsdGlwYW5lbCBwbG90CmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NC41fQoKCiMgbGVnZW5kcwpQSEUucmVnaW9uLmNvbWJpcGxvdC4xLmxlZ2VuZHMgPC0gcGxvdF9ncmlkKGdldF9sZWdlbmQocC5yZWdpb24ueWVhci5idWJibGVwbG90KSwgZ2V0X2xlZ2VuZChwLnJlZ2lvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXApLCBnZXRfbGVnZW5kKHAucmVnaW9uLm9yaWVudGF0aW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCksIGdldF9sZWdlbmQocC5yZWdpb24uaGl2LmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCksIGdldF9sZWdlbmQocC5yZWdpb24uQWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCksIG5yb3c9MSwgYWxpZ249ImgiLCByZWxfd2lkdGhzPWMoNiw0LDQsNCw0KSwgc2NhbGU9MC45NSkKCgojIEFycmFuZ2UgcGxvdHMgdmVydGljYWxseQpwLnllYXIuYnViYmxlcGxvdC5jb21iaSA8LSBwbG90X2dyaWQocC5hbGwueWVhci5idWJibGVwbG90ICsgeC50aGVtZS5zdHJpcCwgcC5yZWdpb24ueWVhci5idWJibGVwbG90ICsgbGVnZW5kLnN0cmlwLCBuY29sPTEsIGFsaWduPSJ2IixheGlzPSJsciIsIHJlbF9oZWlnaHRzPWMoMSw3KSkKCnAucmVnaW9uLmhiYXIuY291bnRzLmNvbWJpIDwtIHBsb3RfZ3JpZChwLmFsbC5oYmFycGxvdCArIHgudGhlbWUuc3RyaXAgKyB5LnRoZW1lLnN0cmlwLCBwLnJlZ2lvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKcC5yZWdpb24uaGJhci5vcmllbnRhdGlvbi5jb21iaSA8LSBwbG90X2dyaWQocC5hbGwub3JpZW50YXRpb24uaGJhcnBsb3QgKyB4LnRoZW1lLnN0cmlwICsgeS50aGVtZS5zdHJpcCwgcC5yZWdpb24ub3JpZW50YXRpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICsgbGVnZW5kLnN0cmlwLCBuY29sPTEsIGFsaWduPSJ2IixheGlzPSJsciIsIHJlbF9oZWlnaHRzPWMoMSw3KSkKCnAucmVnaW9uLmhiYXIuaGl2LmNvbWJpIDwtIHBsb3RfZ3JpZChwLmFsbC5oaXYuaGJhcnBsb3QgKyB4LnRoZW1lLnN0cmlwICsgeS50aGVtZS5zdHJpcCwgcC5yZWdpb24uaGl2LmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArIGxlZ2VuZC5zdHJpcCwgbmNvbD0xLCBhbGlnbj0idiIsYXhpcz0ibHIiLCByZWxfaGVpZ2h0cz1jKDEsNykpCgpwLnJlZ2lvbi5oYmFyLkFnZS5jb21iaSA8LSBwbG90X2dyaWQocC5hbGwuQWdlLmhiYXJwbG90ICsgeC50aGVtZS5zdHJpcCArIHkudGhlbWUuc3RyaXAsIHAucmVnaW9uLkFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKIyBDb21iaW5lIHRoZSBwbG90cwpwLnJlZ2lvbi5oYmFyLmNvbWJpLnBsdXMuYWxsIDwtIHBsb3RfZ3JpZChwLnllYXIuYnViYmxlcGxvdC5jb21iaSwgcC5yZWdpb24uaGJhci5jb3VudHMuY29tYmksIHAucmVnaW9uLmhiYXIub3JpZW50YXRpb24uY29tYmksIHAucmVnaW9uLmhiYXIuaGl2LmNvbWJpLCBwLnJlZ2lvbi5oYmFyLkFnZS5jb21iaSwgbnJvdz0xLCByZWxfd2lkdGhzPWMoNiw0LDQsNCw0KSwgbGFiZWxzID0gYygiQSIsIkIiLCJDIiwiRCIsIkUiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgdmp1c3Q9MC4yNSkKIyBhbmQgYWRkIHRoZSBsZWdlbmRzIG9uIHRvcApwLnJlZ2lvbi5oYmFyLmNvbWJpLnBsdXMuYWxsLndpdGgubGVnZW5kcyA8LSBwbG90X2dyaWQocC5yZWdpb24uaGJhci5jb21iaS5wbHVzLmFsbCwgUEhFLnJlZ2lvbi5jb21iaXBsb3QuMS5sZWdlbmRzLCBuY29sPTEsIHJlbF9oZWlnaHRzPWMoNiwxKSwgc2NhbGUgPSAwLjk1KQoKCgpwLnJlZ2lvbi5oYmFyLmNvbWJpLnBsdXMuYWxsLndpdGgubGVnZW5kcwojZ2dzYXZlKHBhc3RlMChGaWd1cmVfb3V0cHV0X2RpcmVjdG9yeSwgIlN1cEZpZzJfVFBBLVBIRV9TYW1wbGUtbWV0YWRpc3Ryb3MtYnktcGhlX3JlZ2lvbithbGwtY29tYmkuIixmb3JtYXQoU3lzLkRhdGUoKSwiJVklbSVkIiksIi5wZGYiKSwgdW5pdHM9J21tJywgd2lkdGg9MjQwLCBoZWlnaHQ9MTM1LCBkZXZpY2U9J3BkZicsIGRwaT0xMjAwKQoKYGBgClwKXApOb3cgbGV0cyBsb29rIGF0IHNvbWUgZ2VuZXRpYyBkYXRhClwKIyMjIE1ha2UgTUwgdHJlZSB3aXRoIHN1YmxpbmVhZ2UgdGlwcG9pbnRzCmBgYHtyfQpUUEEuTUx0cmVlLmdndHJlZS50aXBwb2ludCA8LSBUUEEuTUx0cmVlLmdndHJlZSAlPCslIGRhdGEuZnJhbWUoU2FtcGxlX05hbWU9VFBBLm1ldGEyLjEkU2FtcGxlX05hbWUsIFN1YmxpbmVhZ2U9VFBBLm1ldGEyLjEkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2Usc3RyaW5nc0FzRmFjdG9ycyA9IEYpICsgCiAgZ2VvbV90aXBwb2ludChhZXMoY29sb3I9U3VibGluZWFnZSksIHNpemU9MC41LCBhbHBoYT0wLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IlN1YmxpbmVhZ2UiLHZhbHVlcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZS5jb2xzLCBicmVha3M9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UpCmBgYApcCkFkZCBtZXRhZGF0YQpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQojIENvbnRpbmVudApwLlRQQS5NTHRyZWUuUEhFIDwtIGdoZWF0bWFwKFRQQS5NTHRyZWUuZ2d0cmVlLnRpcHBvaW50LAogICAgICAgICAgICAgICBUUEEucmF3c2VxLmNvbnRpbmVudHMucCwgY29sb3I9TlVMTCx3aWR0aD0wLjA3NSxvZmZzZXQ9MC4wMDAwMDAyNSwgY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iQ29udGluZW50Iix2YWx1ZXM9Y29udGluZW50YWwuY29scy5icmV3MiRjb250aW5lbnQuY29sLCBicmVha3M9Y29udGluZW50YWwuY29scy5icmV3MiRDb250aW5lbnQsIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSxuY29sPTIpKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICBuZXdfc2NhbGVfZmlsbCgpCgojIGlzIFVLCnAuVFBBLk1MdHJlZS5QSEUgPC0gZ2hlYXRtYXAocC5UUEEuTUx0cmVlLlBIRSwKICAgICAgICAgICAgICAgVFBBLnJhd3NlcS5VSy5wLCBjb2xvcj1OVUxMLHdpZHRoPTAuMDc1LG9mZnNldD0wLjAwMDAxMDI1LCBjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCxmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJFbmdsYW5kL090aGVyIiwgdmFsdWVzPWMoImJsYWNrIiwiZ3JleTk1IiksIGJyZWFrcz1jKCJFbmdsYW5kIiwiT3RoZXIiKSwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyLG5jb2w9MikpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogIG5ld19zY2FsZV9maWxsKCkKCiMgTGluZWFnZQpwLlRQQS5NTHRyZWUuUEhFIDwtIGdoZWF0bWFwKHAuVFBBLk1MdHJlZS5QSEUsVFBBLnJhd3NlcS5MaW5lYWdlLnAsIGNvbG9yPU5VTEwsd2lkdGg9MC4wNzUsb2Zmc2V0PTAuMDAwMDIwMjUsIGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLGZvbnQuc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkxpbmVhZ2UiLHZhbHVlcz1UUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UuY29sLCBicmVha3M9VFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDMsIG5jb2w9MikpICsgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICAgbmV3X3NjYWxlX2ZpbGwoKSArCiAgTlVMTAoKIyBzdWJsaW5lYWdlCnAuVFBBLk1MdHJlZS5QSEUgPC0gZ2hlYXRtYXAocC5UUEEuTUx0cmVlLlBIRSwgZGF0YS5mcmFtZShyb3cubmFtZXM9VFBBLm1ldGEyLjEkU2FtcGxlX05hbWUsIFN1YmxpbmVhZ2U9VFBBLm1ldGEyLjEkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2Usc3RyaW5nc0FzRmFjdG9ycyA9IEYpLCBjb2xvcj1OVUxMLHdpZHRoPTAuMDc1LG9mZnNldD0wLjAwMDAzMDI1LCBjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCxmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJTdWJsaW5lYWdlIix2YWx1ZXM9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UuY29scywgYnJlYWtzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDQsIG5jb2w9MykpICsgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICAgbmV3X3NjYWxlX2ZpbGwoKSArCiAgTlVMTApgYGAKXApwbG90CmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9CnAuVFBBLk1MdHJlZS5QSEUKCiNnZ3NhdmUocGFzdGUwKEZpZ3VyZV9vdXRwdXRfZGlyZWN0b3J5LCAiU3VwRmlnM19UUEEtUEhFX0dsb2JhbF9QaHlsbytVSy1oaWdobGlnaHRzLiIsZm9ybWF0KFN5cy5EYXRlKCksIiVZJW0lZCIpLCIucGRmIiksIHVuaXRzPSdtbScsIHdpZHRoPTE4NSwgaGVpZ2h0PTE2MCwgZGV2aWNlPSdwZGYnLCBkcGk9MTIwMCkKYGBgClwKXAojIyMgR2VvZ3JhcGhpYyBkaXN0cmlidXRpb25zIG9mIExpbmVhZ2VzIGFuZCBTdWJsaW5lYWdlcwpXaGF0IGFib3V0IHN1YmxpbmVhZ2VzPwpgYGB7cn0KcC5yZWdpb24uTGluZWFnZS5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLmdlby5MaW5lYWdlLCBhZXMoQ291bnQscGhlX2NlbnRyZSxmaWxsPVRQQV9MaW5lYWdlKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlRQQVxuTGluZWFnZSIsdmFsdWVzPVRQQV9MaW5lYWdlLmNvbHMkTGluZWFnZS5jb2wsIGJyZWFrcz1UUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UpICsKICBsYWJzKHk9IlVLSFNBIFJlZ2lvbiIsIHg9IlRQQSBMaW5lYWdlIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTMpKSArCiAgI2dlb21fdGV4dChkYXRhPVBIRS5nZW8uTGluZWFnZSwgYWVzKGN1bV9mcmFjdC5taWQsIHBoZV9jZW50cmUsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikgKwogIE5VTEwKCnAucmVnaW9uLnN1YmxpbmVhZ2UuaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5nZW8uc3VibGluZWFnZSwgYWVzKENvdW50LHBoZV9jZW50cmUsZmlsbD1UUEEucGluZWNvbmUuc3VibGluZWFnZSkpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJUUEFcblN1YmxpbmVhZ2UiLHZhbHVlcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZS5jb2xzLCBicmVha3M9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UpICsKICBsYWJzKHk9IlVLSFNBIFJlZ2lvbiIsIHg9IlRQQSBTdWJsaW5lYWdlIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTQpKSArCiAgI2dlb21fdGV4dChkYXRhPVBIRS5nZW8uc3VibGluZWFnZSwgYWVzKGN1bV9mcmFjdC5taWQsIHBoZV9jZW50cmUsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikgKwogIE5VTEwKCmBgYApcCkNvbWJpIHBsb3QgKGdlb2dyYXBoeSBsaW5lYWdlcykKYGBge3J9ClBIRS5yZWdpb24uY29tYmlwbG90LjIubGluZWFnZXMgPC0gcGxvdF9ncmlkKHAucmVnaW9uLnllYXIuYnViYmxlcGxvdCArbGVnZW5kLnN0cmlwLCBwLnJlZ2lvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAgKyBjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDAsMTUwKSksIHAucmVnaW9uLkxpbmVhZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICtsZWdlbmQuc3RyaXAsIHAucmVnaW9uLnN1YmxpbmVhZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICtsZWdlbmQuc3RyaXAsIG5yb3c9MSwgYWxpZ249ImgiLCByZWxfd2lkdGhzPWMoNiwzLDQsNCksIHNjYWxlPTAuOTksIGxhYmVscz1jKCJDIiwiRCIsIkUiLCJGIiksIGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUpCgojIHNlcGFyYXRlIG91dCB0aGUgcGxvdCBmb3IgdGhlIGxlZ2VuZHMKcC5yZWdpb24ueWVhci5idWJibGVwbG90LmxlZ2VuZCA8LSBnZXRfbGVnZW5kKHAucmVnaW9uLnllYXIuYnViYmxlcGxvdCkKcC5yZWdpb24uaGJhcnBsb3QubGVnZW5kIDwtIGdldF9sZWdlbmQocC5yZWdpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwKQpwLnJlZ2lvbi5MaW5lYWdlLmhiYXJwbG90LmxlZ2VuZCA8LSBnZXRfbGVnZW5kKHAucmVnaW9uLkxpbmVhZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwKQpwLnJlZ2lvbi5zdWJsaW5lYWdlLmhiYXJwbG90LmxlZ2VuZCA8LSBnZXRfbGVnZW5kKHAucmVnaW9uLnN1YmxpbmVhZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwKQoKUEhFLnJlZ2lvbi5jb21iaXBsb3QuMi5saW5lYWdlcy5sZWdlbmQgPC0gcGxvdF9ncmlkKHAucmVnaW9uLnllYXIuYnViYmxlcGxvdC5sZWdlbmQsIHAucmVnaW9uLmhiYXJwbG90LmxlZ2VuZCwgcC5yZWdpb24uTGluZWFnZS5oYmFycGxvdC5sZWdlbmQsIHAucmVnaW9uLnN1YmxpbmVhZ2UuaGJhcnBsb3QubGVnZW5kLCBucm93PTEsIGFsaWduPSJoIiwgcmVsX3dpZHRocz1jKDYsMyw0LDQpKQoKUEhFLnJlZ2lvbi5jb21iaXBsb3QuMi5saW5lYWdlcyA8LSBwbG90X2dyaWQoUEhFLnJlZ2lvbi5jb21iaXBsb3QuMi5saW5lYWdlcywgUEhFLnJlZ2lvbi5jb21iaXBsb3QuMi5saW5lYWdlcy5sZWdlbmQsIHJlbF9oZWlnaHRzID0gYyg0LDEpLCBuY29sPTEpCgpQSEUucmVnaW9uLmNvbWJpcGxvdC4yLmxpbmVhZ2VzCmBgYApcCk9LLCBsZXQncyBub3cgYWRkIGEgbWFwIG9mIHRoZXNlIGdlb2dyYXBoaWNhbCBkaXN0cmlidXRpb25zCgpcCkxldCdzIHVzZWQgT05TIHB1Ymxpc2hlZCBzaGFwZSBmaWxlcyAtIHRoZXJlIGlzIG9uZSBhdmFpbGFibGUgdGhhdCBzaG93cyBQdWJsaWMgSGVhbHRoIEVuZ2xhbmQgcmVnaW9uIGJvdW5kYXJpZXMuIApgYGB7cn0KCiMgR2VuZXJhdGUgYXBwcm94aW1hdGUgcmVnaW9uYWwgR1BTIGNvb3JkcwpQSEUucmVnaW9uLkdQUyA8LSBkYXRhLmZyYW1lKAogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwKICAgICAgICAgIHBoZV9jZW50cmUgPSBjKCJFYXN0IE1pZGxhbmRzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICJFYXN0IG9mIEVuZ2xhbmQiLCJMb25kb24iLCJOb3J0aCBFYXN0IiwiTm9ydGggV2VzdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiU291dGggRWFzdCIsIlNvdXRoIFdlc3QiLCJXZXN0IE1pZGxhbmRzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICJZb3Jrc2hpcmUgYW5kIEh1bWJlciIsIlVLIChub3QgRW5nbGFuZCkiLCJOb3QgS25vd24iKSwKICAgICAgICAgICAgTG9uZ2l0dWRlID0gYygtMC43LDAuNSwtMC4yLC0xLjksLTIuNCwKICAgICAgICAgICAgICAgICAgICAgICAgIDAuMDUsLTIuOSwtMiwtMC44LDAuMSwwLjYzKSwKICAgICAgICAgICBMYXRpdHVkZSA9IGMoNTIuOSw1Mi40LDUxLjUsNTUsNTMuNywKICAgICAgICAgICAgICAgICAgICAgICAgIDUxLjEsNTEsNTIuNiw1My44LDU0LjcsNTQuMSkKICApICAKUEhFLnJlZ2lvbi5HUFMgPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLCBQSEUuZ2VvLkxpbmVhZ2VbUEhFLmdlby5MaW5lYWdlJFRQQV9MaW5lYWdlPT0iU1MxNCIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTKVs0XSA8LSAiU1MxNCIKUEhFLnJlZ2lvbi5HUFMgPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLCBQSEUuZ2VvLkxpbmVhZ2VbUEhFLmdlby5MaW5lYWdlJFRQQV9MaW5lYWdlPT0iTmljaG9scyIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTKVs1XSA8LSAiTmljaG9scyIKUEhFLnJlZ2lvbi5HUFNbaXMubmEoUEhFLnJlZ2lvbi5HUFMpXSA8LSAwCgpQSEUucmVnaW9uLkdQUyA8LSBsZWZ0X2pvaW4oUEhFLnJlZ2lvbi5HUFMsIFBIRS5nZW8uTGluZWFnZVtQSEUuZ2VvLkxpbmVhZ2UkVFBBX0xpbmVhZ2U9PSJTUzE0IixjKCJwaGVfY2VudHJlIiwidG90YWwucmVnaW9uIildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTKVs2XSA8LSAiUmVnaW9uX0NvdW50IgoKUEhFLnJlZ2lvbi5HUFMkcmFkaXVzIDwtIDAuNSooMS0xL3NxcnQoUEhFLnJlZ2lvbi5HUFMkUmVnaW9uX0NvdW50KSkKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgSW1wb3J0IGRhdGFmaWxlIGZyb20gaHR0cHM6Ly9nZW9wb3J0YWwuc3RhdGlzdGljcy5nb3YudWsvZGF0YXNldHMvcHVibGljLWhlYWx0aC1lbmdsYW5kLWNlbnRyZXMtZGVjZW1iZXItMjAxNi1mdWxsLWNsaXBwZWQtYm91bmRhcmllcy1pbi1lbmdsYW5kL2V4cGxvcmU/bG9jYXRpb249NTIuOTUwMDAwJTJDLTIuMDAwMDAwJTJDNi44OAoKVUsuc2hhcGVmaWxlIDwtIHJlYWRPR1IoZHNuPVVLLnB1YmxpY2hlYWx0aC5zaGFwZWZpbGUuZGF0YSkKCiNSZXNoYXBlIGZvciBnZ3Bsb3QyIHVzaW5nIHRoZSBCcm9vbSBwYWNrYWdlClVLLm1hcGRhdGEgPC0gdGlkeShVSy5zaGFwZWZpbGUsIHJlZ2lvbj0icGhlYzE2bm0iKQoKI1VLLmdnIDwtIGdncGxvdCgpICsgZ2VvbV9wb2x5Z29uKGRhdGEgPSBVSy5tYXBkYXRhLCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApLCBjb2xvciA9ICIjRkZGRkZGIiwgc2l6ZSA9IDAuMjUpClVLLmdnIDwtIGdncGxvdCgpICsgZ2VvbV9wb2x5Z29uKGRhdGEgPSBVSy5tYXBkYXRhLCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApLCBjb2xvcj0iZ3JleTI1IiwgZmlsbD0iZ3JleTkwIiwgc2l6ZSA9IDAuMDc1KQoKVUsuZ2cgPC0gVUsuZ2cgKyBjb29yZF9maXhlZCgxKSArIHRoZW1lX25vdGhpbmcoKQojVUsuZ2cKCiMgQ29udmVydCBVSyByZWdpb25zIHRvIGJlIGNvbXBhdGlibGUgd2l0aCBtYXAKIyBGaXJzdCBmaW5kIGNlbnRyZSBwb2ludCBmb3IgZWFjaCByZWdpb24KVUsubWFwZGF0YS5yZWdpb25zLm1lYW5jb29yZHMgPC0gVUsubWFwZGF0YSAlPiUgZHBseXI6Omdyb3VwX2J5KGlkKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKG1lYW4ubGF0PW1lYW4obGF0KSwgbWVhbi5sb25nPW1lZGlhbihsb25nKSkgJT4lCiAgZHBseXI6OnVuZ3JvdXAoKQpjb2xuYW1lcyhVSy5tYXBkYXRhLnJlZ2lvbnMubWVhbmNvb3JkcylbMV0gPC0gInBoZV9jZW50cmUiCgpQSEUucmVnaW9uLkdQUy51a21hcCA8LSBkcGx5cjo6bGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLCBVSy5tYXBkYXRhLnJlZ2lvbnMubWVhbmNvb3JkcywgYnk9InBoZV9jZW50cmUiKQoKIyBBZGQgYXJ0aWZpY2lhbCBsb2NhdGlvbiBmb3IgJ25vdCBrbm93bicKUEhFLnJlZ2lvbi5HUFMudWttYXBbUEhFLnJlZ2lvbi5HUFMudWttYXAkcGhlX2NlbnRyZT09Ik5vdCBLbm93biIsIm1lYW4ubGF0Il0gPC0gNjAwMDAwClBIRS5yZWdpb24uR1BTLnVrbWFwW1BIRS5yZWdpb24uR1BTLnVrbWFwJHBoZV9jZW50cmU9PSJOb3QgS25vd24iLCJtZWFuLmxvbmciXSA8LSA1NTAwMDAKCiMgU2hpZnQgIlNvdXRoIEVhc3QiIHNsaWdodGx5IHRvIHJlZHVjZSB0aGUgb3ZlcmxhcCB3aXRoIExvbmRvbgpQSEUucmVnaW9uLkdQUy51a21hcFtQSEUucmVnaW9uLkdQUy51a21hcCRwaGVfY2VudHJlPT0iU291dGggRWFzdCIsIm1lYW4ubG9uZyJdIDwtIDQ3NTAwMAojIFNoaWZ0ICJFYXN0IG9mIEVuZ2xhbmQgRWFzdCIgc2xpZ2h0bHkgdG8gcmVkdWNlIHRoZSBvdmVybGFwIHdpdGggTG9uZG9uIApQSEUucmVnaW9uLkdQUy51a21hcFtQSEUucmVnaW9uLkdQUy51a21hcCRwaGVfY2VudHJlPT0iRWFzdCBvZiBFbmdsYW5kIiwibWVhbi5sYXQiXSA8LSAyNzUwMDAKCiMgTm90IGdvaW5nIHRvIHRyeSBwbG90dGluZyB0aGUgMiBzYW1wbGVzIGZyb20gZWxzZXdoZXJlIGluIHRoZSBVSywgc28gcmVtb3ZlIHRoYXQgcm93ClBIRS5yZWdpb24uR1BTLnVrbWFwIDwtIFBIRS5yZWdpb24uR1BTLnVrbWFwW1BIRS5yZWdpb24uR1BTLnVrbWFwJHBoZV9jZW50cmUgIT0gIlVLIChub3QgRW5nbGFuZCkiLF0KCiMgQ3JlYXRlIHJhZGl1cyB2YXJpYWJsZSBmb3IgcGxvdHRpbmcgcGllIHNpemVzICh1c2UgbG9nMTAobikqMjAsMDAwKQpQSEUucmVnaW9uLkdQUy51a21hcCRyYWRpdXMuVUsgPC0gbG9nMTAoUEhFLnJlZ2lvbi5HUFMudWttYXAkUmVnaW9uX0NvdW50KSoyMDAwMAoKI1BIRS5nZW8uY291bnQueWVhcnMubGluZWFnZQoKVUsuZ2cuc2NhdHRlcnBpZSA8LSBVSy5nZyArIGdlb21fc2NhdHRlcnBpZShkYXRhPVBIRS5yZWdpb24uR1BTLnVrbWFwLCBhZXMobWVhbi5sb25nLCBtZWFuLmxhdCwgZ3JvdXA9cGhlX2NlbnRyZSwgcj1yYWRpdXMuVUspLCBhbHBoYT0wLjg1LCBjb2xvcj1OQSwgY29scz1jKCJOaWNob2xzIiwiU1MxNCIpKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlRQQVxuTGluZWFnZSIsdmFsdWVzPVRQQV9MaW5lYWdlLmNvbHMkTGluZWFnZS5jb2wsIGJyZWFrcz1UUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiKQoKVUsuZ2cuc2NhdHRlcnBpZSA8LSBVSy5nZy5zY2F0dGVycGllICsgZ2VvbV9zY2F0dGVycGllX2xlZ2VuZChQSEUucmVnaW9uLkdQUy51a21hcFshaXMubmEoUEhFLnJlZ2lvbi5HUFMudWttYXAkbWVhbi5sYXQpLCJyYWRpdXMuVUsiXSwgbGFiZWxsZXI9ZnVuY3Rpb24oeCkgcm91bmQoKDEwXih4LzIwMDAwKSksMCksIG49MywgeD0xNTAwMDAsIHk9NTAwMDAwKQoKVUsuZ2cuc2NhdHRlcnBpZSA8LSBVSy5nZy5zY2F0dGVycGllICsgdGhlbWVfbm90aGluZygpCgojPyBBZGQgbGFiZWxzClVLLmdnLnNjYXR0ZXJwaWUubGFicyA8LSBVSy5nZy5zY2F0dGVycGllICsgZ2VvbV9sYWJlbF9yZXBlbChkYXRhPVBIRS5yZWdpb24uR1BTLnVrbWFwWyFpcy5uYShQSEUucmVnaW9uLkdQUy51a21hcCRtZWFuLmxhdCksXSwgYWVzKG1lYW4ubG9uZywgbWVhbi5sYXQsIGxhYmVsPXBoZV9jZW50cmUpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIG51ZGdlX3ggPSA1MDAwMCwgbnVkZ2VfeSA9IC0yNTAwMCwgc2VnbWVudC5zaXplICA9IDAuMSkgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSwgbGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKSArIAogIHRoZW1lLnRleHQuc2l6ZSArCiAgdGhlbWVfbm90aGluZygpCgpVSy5nZy5zY2F0dGVycGllLmxhYnMKYGBgClwKXApOb3cgZG8gYW4gZXF1aXZhbGVudCBwbG90IGZvciBzdWJsaW5lYWdlcwpgYGB7cn0KUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluIDwtIFBIRS5yZWdpb24uR1BTLnVrbWFwCgoKUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4sIFBIRS5nZW8uc3VibGluZWFnZVtQSEUuZ2VvLnN1YmxpbmVhZ2UkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2U9PSIxIixjKCJwaGVfY2VudHJlIiwiQ291bnQiKV0sIGJ5PSJwaGVfY2VudHJlIikKY29sbmFtZXMoUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluKVsxMV0gPC0gIjEiClBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiA8LSBsZWZ0X2pvaW4oUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluLCBQSEUuZ2VvLnN1YmxpbmVhZ2VbUEhFLmdlby5zdWJsaW5lYWdlJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlPT0iMiIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbilbMTJdIDwtICIyIgpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4gPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiwgUEhFLmdlby5zdWJsaW5lYWdlW1BIRS5nZW8uc3VibGluZWFnZSRUUEEucGluZWNvbmUuc3VibGluZWFnZT09IjMiLGMoInBoZV9jZW50cmUiLCJDb3VudCIpXSwgYnk9InBoZV9jZW50cmUiKQpjb2xuYW1lcyhQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4pWzEzXSA8LSAiMyIKUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4sIFBIRS5nZW8uc3VibGluZWFnZVtQSEUuZ2VvLnN1YmxpbmVhZ2UkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2U9PSI2IixjKCJwaGVfY2VudHJlIiwiQ291bnQiKV0sIGJ5PSJwaGVfY2VudHJlIikKY29sbmFtZXMoUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluKVsxNF0gPC0gIjYiClBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiA8LSBsZWZ0X2pvaW4oUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluLCBQSEUuZ2VvLnN1YmxpbmVhZ2VbUEhFLmdlby5zdWJsaW5lYWdlJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlPT0iOCIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbilbMTVdIDwtICI4IgpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4gPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiwgUEhFLmdlby5zdWJsaW5lYWdlW1BIRS5nZW8uc3VibGluZWFnZSRUUEEucGluZWNvbmUuc3VibGluZWFnZT09IjE0IixjKCJwaGVfY2VudHJlIiwiQ291bnQiKV0sIGJ5PSJwaGVfY2VudHJlIikKY29sbmFtZXMoUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluKVsxNl0gPC0gIjE0IgpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4gPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiwgUEhFLmdlby5zdWJsaW5lYWdlW1BIRS5nZW8uc3VibGluZWFnZSRUUEEucGluZWNvbmUuc3VibGluZWFnZT09IjE1IixjKCJwaGVfY2VudHJlIiwiQ291bnQiKV0sIGJ5PSJwaGVfY2VudHJlIikKY29sbmFtZXMoUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluKVsxN10gPC0gIjE1IgpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4gPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiwgUEhFLmdlby5zdWJsaW5lYWdlW1BIRS5nZW8uc3VibGluZWFnZSRUUEEucGluZWNvbmUuc3VibGluZWFnZT09IjE2IixjKCJwaGVfY2VudHJlIiwiQ291bnQiKV0sIGJ5PSJwaGVfY2VudHJlIikKY29sbmFtZXMoUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluKVsxOF0gPC0gIjE2IgpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4gPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiwgUEhFLmdlby5zdWJsaW5lYWdlW1BIRS5nZW8uc3VibGluZWFnZSRUUEEucGluZWNvbmUuc3VibGluZWFnZT09IlNpbmdsZXRvbiIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbilbMTldIDwtICJTaW5nbGV0b24iClBIRS5yZWdpb24uR1BTLnVrbWFwLnN1Ymxpbltpcy5uYShQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4pXSA8LSAwCgojIE1vc3Qgc2FtcGxlcyBhcmUgZWl0aGVyIHN1YmxpbmVhZ2UgMSBvciAxNC4gTGV0J3MgY3JlYXRlIGEgY291bnQgb2Ygc2FtcGxlcyB0aGF0IGFyZSBuZWl0aGVyLgpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4kYE90aGVyIFN1YmxpbmVhZ2VzYCA8LSBzYXBwbHkoMTpucm93KFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiksIGZ1bmN0aW9uICh4KSBQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4kUmVnaW9uX0NvdW50W3hdLXN1bShQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4kYDFgW3hdLCBQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4kYDE0YFt4XSkpIAoKCgpVSy5nZy5zY2F0dGVycGllLnN1YmxpbmVhZ2UgPC0gVUsuZ2cgKyBnZW9tX3NjYXR0ZXJwaWUoZGF0YT1QSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW5bUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluJG1lYW4ubG9uZyE9MCxdLCBhZXMobWVhbi5sb25nLCBtZWFuLmxhdCwgZ3JvdXA9cGhlX2NlbnRyZSwgcj1yYWRpdXMuVUspLCBhbHBoYT0wLjg1LCBjb2xvcj1OQSwgY29scz1jKCIxIiwiMTQiLCJPdGhlciBTdWJsaW5lYWdlcyIpKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlRQQVxuU3VibGluZWFnZSIsdmFsdWVzPWMoIiNGQzkyNzIiLCIjQkNCRERDIiwgImdyZXk1MCIpLCBicmVha3M9YygiMSIsIjE0IiwiT3RoZXIgU3VibGluZWFnZXMiKSkKCiMgYWRkIGxlZ2VuZApVSy5nZy5zY2F0dGVycGllLnN1YmxpbmVhZ2UgPC0gVUsuZ2cuc2NhdHRlcnBpZS5zdWJsaW5lYWdlICsgZ2VvbV9zY2F0dGVycGllX2xlZ2VuZChQSEUucmVnaW9uLkdQUy51a21hcFshaXMubmEoUEhFLnJlZ2lvbi5HUFMudWttYXAkbWVhbi5sYXQpLCJyYWRpdXMuVUsiXSwgbGFiZWxsZXI9ZnVuY3Rpb24oeCkgcm91bmQoKDEwXih4LzIwMDAwKSksMCksIG49MywgeD0xNTAwMDAsIHk9NTAwMDAwKQoKI1VLLmdnLnNjYXR0ZXJwaWUgPC0gVUsuZ2cuc2NhdHRlcnBpZSArIHgudGhlbWUuc3RyaXAgKyB5LnRoZW1lLnN0cmlwClVLLmdnLnNjYXR0ZXJwaWUuc3VibGluZWFnZSA8LSBVSy5nZy5zY2F0dGVycGllLnN1YmxpbmVhZ2UgKyB0aGVtZV9ub3RoaW5nKCkKCiM/IEFkZCBsYWJlbHMKVUsuZ2cuc2NhdHRlcnBpZS5zdWJsaW5lYWdlIDwtIFVLLmdnLnNjYXR0ZXJwaWUuc3VibGluZWFnZSArIGdlb21fbGFiZWxfcmVwZWwoZGF0YT1QSEUucmVnaW9uLkdQUy51a21hcFshaXMubmEoUEhFLnJlZ2lvbi5HUFMudWttYXAkbWVhbi5sYXQpLF0sIGFlcyhtZWFuLmxvbmcsIG1lYW4ubGF0LCBsYWJlbD1waGVfY2VudHJlKSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBudWRnZV94ID0gNTAwMDAsIG51ZGdlX3kgPSAtMjUwMDAsIHNlZ21lbnQuc2l6ZSAgPSAwLjEpICsKICB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSwgbGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKSArIAogIHRoZW1lLnRleHQuc2l6ZSArCiAgdGhlbWVfbm90aGluZygpCgoKVUsuZ2cuc2NhdHRlcnBpZS5zdWJsaW5lYWdlCmBgYAoKXApDb21iaW5lZCBtYXAgcGxvdApgYGB7cn0KVUsuZ2cuc2NhdHRlcnBpZS5jb21iaSA8LSBwbG90X2dyaWQoVUsuZ2cuc2NhdHRlcnBpZS5sYWJzLCBVSy5nZy5zY2F0dGVycGllLnN1YmxpbmVhZ2UsIG5jb2w9MiwgbGFiZWxzID0gYygiQSIsIkIiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSkKClVLLmdnLnNjYXR0ZXJwaWUuY29tYmkKYGBgClwKXApQbG90IGluIGNvbWJpbmF0aW9uIHdpdGggYmFycGxvdHMKYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTh9CnBsb3RfZ3JpZChVSy5nZy5zY2F0dGVycGllLmNvbWJpLCBQSEUucmVnaW9uLmNvbWJpcGxvdC4yLmxpbmVhZ2VzLCBucm93PTIsIHJlbF9oZWlnaHRzPWMoNCw1KSkKCiNnZ3NhdmUocGFzdGUwKEZpZ3VyZV9vdXRwdXRfZGlyZWN0b3J5LCJGaWcyX1RQQS1QSEVfTWFwLUxpbmVhZ2UrQmFycGxvdHMuIixmb3JtYXQoU3lzLkRhdGUoKSwiJVklbSVkIiksIi5wZGYiKSwgdW5pdHM9J21tJywgd2lkdGg9MTkwLCBoZWlnaHQ9MTg1LCBkZXZpY2U9J3BkZicsIGRwaT0xMjAwKQpgYGAKXApcCiMjIyBBbmFseXNpcyBieSBzdWJsaW5lYWdlClwKTm93IGxldHMgc3RhcnQgZXhwbG9yaW5nIGhvdyBzYW1wbGVzIGFyZSBkaXN0cmlidXRlZCBieSBzdWJsaW5lYWdlCgpgYGB7cn0KUEhFLm1ldGFkYXRhLmxpbmtlZCA8LSBQSEUubWV0YWRhdGEubGlua2VkClBIRS5tZXRhZGF0YS5saW5rZWQkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UgPC0gZmFjdG9yKFBIRS5tZXRhZGF0YS5saW5rZWQkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGxldmVscz1yZXYoYXMuY2hhcmFjdGVyKHNvcnQodW5pcXVlKFBIRS5tZXRhZGF0YS5saW5rZWQkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpKSkpKQoKUEhFLkxpbmVhZ2UuY291bnQgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFRQQV9MaW5lYWdlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbD1zdW0oQ291bnQpLCBwZXJjPShDb3VudC90b3RhbCkqMTAwKQoKUEhFLnN1Ymxpbi5jb3VudCA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsPXN1bShDb3VudCksIHBlcmM9KENvdW50L3RvdGFsKSoxMDApCgpQSEUuZ2VvLnN1Ymxpbi55ZWFycyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UseWVhcikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpCgoKIyMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBzdWJsaW5lYWdlIGdyb3VwcwoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IGdlbmRlciBvcmllbnRhdGlvbgpQSEUuc3VibGluZWFnZS5vcmllbnRhdGlvbi5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLGdlbmRlcl9vcmllbnRhdGlvbikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuc3VibGluPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoZ2VuZGVyX29yaWVudGF0aW9uKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwuc3VibGluLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCgoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IFVLIGJvcm4KUEhFLnN1YmxpbmVhZ2UuVUtib3JuIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgdWtib3JuKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50KSkgJT4lCiAgI2RwbHlyOjphcnJhbmdlKGRlc2ModWtib3JuKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2ModWtib3JuKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwuc3VibGluLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCiAgCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBMb25kb24gYmFzZWQKUEhFLnN1YmxpbmVhZ2UuTG9uZG9uIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgbG9uZG9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhsb25kb24pLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5zdWJsaW4sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBBZ2UgZ3JvdXAKUEhFLnN1YmxpbmVhZ2UuQWdlIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgYWdlX2dyb3VwKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhhZ2VfZ3JvdXApLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5zdWJsaW4sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBISVYgZ3JvdXAKUEhFLnN1YmxpbmVhZ2UuSElWIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgaGl2cG9zKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhoaXZwb3MpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5zdWJsaW4sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBieSBQSEUgUmVnaW9uClBIRS5zdWJsaW5lYWdlLlBIRWNlbnRyZSA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIHBoZV9jZW50cmUpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnN1Ymxpbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKHBoZV9jZW50cmUpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5zdWJsaW4sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCmBgYAoKXApQbG90IGJ5IHN1YmxpbmVhZ2UKYGBge3J9CnAuc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QgPC0gZ2dwbG90KFBIRS5nZW8uc3VibGluLnllYXJzLCBhZXMoYXMubnVtZXJpYyh5ZWFyKSwgVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGNvbG91cj1UUEEucGluZWNvbmUuc3VibGluZWFnZSkpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNjUsIGFlcyhzaXplPUNvdW50KSkgKyAKICBnZW9tX2xpbmUoYWxwaGE9MC4yNSkgKwogIGd1aWRlcyhjb2xvdXI9J25vbmUnKSArCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gNyxicmVha3M9YygxLDUsMTAsMjUsNTApKSArIAogIGd1aWRlcyhzaXplPWd1aWRlX2xlZ2VuZChucm93PTIsIGRpcmVjdGlvbiA9ICdob3Jpem9udGFsJywgYnlyb3c9VCkpICsKICB0aGVtZV9saWdodCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iVFBBXG5TdWJsaW5lYWdlIix2YWx1ZXM9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UuY29scywgYnJlYWtzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgbGFicyh5PSJUUEEgU3VibGluZWFnZSIsIHg9IlNhbXBsZSBZZWFyIiwgc2l6ZT0iQ291bnQiKSAKI3Auc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QKCnAuc3VibGluZWFnZS5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLnN1Ymxpbi5jb3VudCwgYWVzKENvdW50LFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLGZpbGw9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249InN0YWNrIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlRQQVxuU3VibGluZWFnZSIsdmFsdWVzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMsIGJyZWFrcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSkgKwogIGxhYnMoeT0iVFBBIFN1YmxpbmVhZ2UiLCB4PSJTYW1wbGUgQ291bnQiKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLnN1Ymxpbi5jb3VudCwgYWVzKChDb3VudCsxMiksIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICAjY29vcmRfY2FydGVzaWFuKHhsaW09YygwLDIwMCkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDAsMjYwKSkgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChuY29sPTIpKQojcC5zdWJsaW5lYWdlLmhiYXJwbG90IAoKcC5zdWJsaW5lYWdlLm9yaWVudGF0aW9uLmhiYXJwbG90IDwtIGdncGxvdChQSEUuc3VibGluZWFnZS5vcmllbnRhdGlvbi5jb3VudHMsIGFlcyh5PVRQQS5waW5lY29uZS5zdWJsaW5lYWdlLHg9Q291bnQsZmlsbD1nZW5kZXJfb3JpZW50YXRpb24pKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iT3JpZW50YXRpb24iLHZhbHVlcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbi5jb2xzLCBicmVha3M9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24pICsKICBsYWJzKHk9IlRQQSBTdWJsaW5lYWdlIiwgeD0iT3JpZW50YXRpb24iKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5jb2w9MSkpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuc3VibGluZWFnZS5vcmllbnRhdGlvbi5jb3VudHMsIGFlcyhjdW1fZnJhY3QubWlkLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSxsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5yZWdpb24ub3JpZW50YXRpb24uaGJhcnBsb3QKCnAuc3VibGluZWFnZS5oaXYuaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5zdWJsaW5lYWdlLkhJViwgYWVzKHk9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIHg9Q291bnQsZmlsbD1oaXZwb3MpKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iSElWICt2ZSIsdmFsdWVzPVBIRS5oaXYuY29scyRoaXYuY29scywgYnJlYWtzPVBIRS5oaXYuY29scyRoaXZwb3MpICsKICBsYWJzKHk9IlRQQSBTdWJsaW5lYWdlIiwgeD0iSElWICt2ZSIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbD0xKSkgKyAKICBnZW9tX3RleHQoZGF0YT1QSEUuc3VibGluZWFnZS5ISVYsIGFlcyhjdW1fZnJhY3QubWlkLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSxsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5zdWJsaW5lYWdlLmhpdi5oYmFycGxvdAoKcC5zdWJsaW5lYWdlLnVrYm9ybi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLnN1YmxpbmVhZ2UuVUtib3JuLCBhZXMoeT1UUEEucGluZWNvbmUuc3VibGluZWFnZSx4PUNvdW50LGZpbGw9dWtib3JuKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlVLXG5ib3JuIix2YWx1ZXM9UEhFLnVrYm9ybi5jb2xzJHVrYm9ybi5jb2xzLCBicmVha3M9UEhFLnVrYm9ybi5jb2xzJHVrYm9ybikgKwogIGxhYnMoeT0iVFBBIFN1YmxpbmVhZ2UiLCB4PSJVSyBib3JuIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTMpKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLnN1YmxpbmVhZ2UuVUtib3JuLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3Auc3VibGluZWFnZS51a2Jvcm4uaGJhcnBsb3QKCnAuc3VibGluZWFnZS5BZ2UuaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5zdWJsaW5lYWdlLkFnZSwgYWVzKHk9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIHg9Q291bnQgLGZpbGw9YWdlX2dyb3VwKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkFnZVxuR3JvdXAiLHZhbHVlcz1QSEUuQWdlLmNvbHMkYWdlX2dyb3VwLmNvbHMsIGJyZWFrcz1QSEUuQWdlLmNvbHMkYWdlX2dyb3VwKSArCiAgbGFicyh5PSJUUEEgU3VibGluZWFnZSIsIHg9IkFnZSBHcm91cCIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbD0xKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5zdWJsaW5lYWdlLkFnZSwgYWVzKGN1bV9mcmFjdC5taWQsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpCiNwLnN1YmxpbmVhZ2UuQWdlLmhiYXJwbG90CgoKcC5zdWJsaW5lYWdlLlBIRXJlZ2lvbi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLnN1YmxpbmVhZ2UuUEhFY2VudHJlLCBhZXMoeT1UUEEucGluZWNvbmUuc3VibGluZWFnZSwgeD1Db3VudCwgZmlsbD1waGVfY2VudHJlKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlVLSFNBXG5SZWdpb24iLHZhbHVlcz1QSEUucmVnaW9uLmNvbHMuYnJldyRyZWdpb24uY29sLCBicmVha3M9UEhFLnJlZ2lvbi5jb2xzLmJyZXckUEhFLnJlZ2lvbikgKwogIGxhYnMoeT0iVFBBIFN1YmxpbmVhZ2UiLCB4PSJVS0hTQSBSZWdpb24iKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9NCkpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuc3VibGluZWFnZS5QSEVjZW50cmUsIGFlcyhjdW1fZnJhY3QubWlkLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSxsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQoKYGBgClwKTG9vayBhdCBob3cgc3VibGluZWFnZXMgYXJlIGRpc3RyaWJ1dGVkIGJ5IHJlZ2lvbiAoc3VibGluZWFnZS1jZW50cmljKQpgYGB7cn0KcC5zdWJsaW5lYWdlLlBIRXJlZ2lvbi5oYmFycGxvdApgYGAKClwKQ29tYmluZSBwYXRpZW50IG1ldGFkYXRhIGludG8gYSBwbG90CmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTB9CiNQSEUuc3VibGluZWFnZXMuY29tYmlwbG90LjEgPC0gcGxvdF9ncmlkKHAuc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QsIHAuc3VibGluZWFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5vcmllbnRhdGlvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5oaXYuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2UuUEhFcmVnaW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLnVrYm9ybi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5BZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBucm93PTEsIGFsaWduPSJoIiwgcmVsX3dpZHRocz1jKDMsMiwyLDIsMiwyLDIpLCBzY2FsZT0wLjkpCgojUEhFLnN1YmxpbmVhZ2VzLmNvbWJpcGxvdC4xIDwtIHBsb3RfZ3JpZChwLnN1YmxpbmVhZ2UueWVhci5idWJibGVwbG90LCBwLnN1YmxpbmVhZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2Uub3JpZW50YXRpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2UuaGl2LmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLkFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5QSEVyZWdpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBucm93PTEsIGFsaWduPSJoIiwgcmVsX3dpZHRocz1jKDMsMiwyLDIsMiw0KSwgc2NhbGU9MC45KQoKUEhFLnN1YmxpbmVhZ2VzLmNvbWJpcGxvdC4xIDwtIHBsb3RfZ3JpZChwLnN1YmxpbmVhZ2UueWVhci5idWJibGVwbG90LCBwLnN1YmxpbmVhZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2Uub3JpZW50YXRpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2UuaGl2LmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLkFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIG5yb3c9MSwgYWxpZ249ImgiLCByZWxfd2lkdGhzPWMoNCwyLDIsMiwyKSwgc2NhbGU9MC45KQoKUEhFLnN1YmxpbmVhZ2VzLmNvbWJpcGxvdC4xIAoKYGBgCgoKXApMZXRzIGFkZCB0aGUgJ2FsbCcgcm93IGFnYWluIHRvIHRoZSAnYnkgc3VibGluZWFnZScgcGxvdApgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTJ9CiMgbGVnZW5kcwpQSEUuc3VibGluZWFnZS5jb21iaXBsb3QuMS5sZWdlbmRzIDwtIHBsb3RfZ3JpZChnZXRfbGVnZW5kKHAuc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QpLCBnZXRfbGVnZW5kKHAuc3VibGluZWFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXApLCBnZXRfbGVnZW5kKHAuc3VibGluZWFnZS5vcmllbnRhdGlvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXApLCBnZXRfbGVnZW5kKHAuc3VibGluZWFnZS5oaXYuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwKSwgZ2V0X2xlZ2VuZChwLnN1YmxpbmVhZ2UuQWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCksIG5yb3c9MSwgYWxpZ249ImgiLCByZWxfd2lkdGhzPWMoNiw0LDQsNCw0KSwgc2NhbGU9MC45NSkKCiMgcmVnaW9ucwojUEhFLnN1YmxpbmVhZ2UuY29tYmlwbG90LjEubm9sZWdlbmQgPC0gcGxvdF9ncmlkKHAuc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QgKyBsZWdlbmQuc3RyaXAsIHAuc3VibGluZWFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIHAuc3VibGluZWFnZS5vcmllbnRhdGlvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIHAuc3VibGluZWFnZS5oaXYuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICsgbGVnZW5kLnN0cmlwLCBwLnN1YmxpbmVhZ2UuQWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArIGxlZ2VuZC5zdHJpcCwgbnJvdz0xLCBhbGlnbj0iaCIsIHJlbF93aWR0aHM9Yyg0LDIsMiwyLDIpLCBzY2FsZT0wLjkpCgojIE9yIGRvIGl0IHZlcnRpY2FsbHkKcC5zdWJsaW5lYWdlLnllYXIuYnViYmxlcGxvdC5jb21iaSA8LSBwbG90X2dyaWQocC5hbGwueWVhci5idWJibGVwbG90ICsgeC50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLnllYXIuYnViYmxlcGxvdCArIGxlZ2VuZC5zdHJpcCwgbmNvbD0xLCBhbGlnbj0idiIsYXhpcz0ibHIiLCByZWxfaGVpZ2h0cz1jKDEsNykpCgpwLnN1YmxpbmVhZ2UuaGJhci5jb3VudHMuY29tYmkgPC0gcGxvdF9ncmlkKHAuYWxsLmhiYXJwbG90ICsgeC50aGVtZS5zdHJpcCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKcC5zdWJsaW5lYWdlLmhiYXIub3JpZW50YXRpb24uY29tYmkgPC0gcGxvdF9ncmlkKHAuYWxsLm9yaWVudGF0aW9uLmhiYXJwbG90ICsgeC50aGVtZS5zdHJpcCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5vcmllbnRhdGlvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKcC5zdWJsaW5lYWdlLmhiYXIuaGl2LmNvbWJpIDwtIHBsb3RfZ3JpZChwLmFsbC5oaXYuaGJhcnBsb3QgKyB4LnRoZW1lLnN0cmlwICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLmhpdi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKcC5zdWJsaW5lYWdlLmhiYXIuQWdlLmNvbWJpIDwtIHBsb3RfZ3JpZChwLmFsbC5BZ2UuaGJhcnBsb3QgKyB4LnRoZW1lLnN0cmlwICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLkFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKIyBDb21iaW5lIHRoZSBwbG90cwpwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbCA8LSBwbG90X2dyaWQocC5zdWJsaW5lYWdlLnllYXIuYnViYmxlcGxvdC5jb21iaSwgcC5zdWJsaW5lYWdlLmhiYXIuY291bnRzLmNvbWJpLCBwLnN1YmxpbmVhZ2UuaGJhci5vcmllbnRhdGlvbi5jb21iaSwgcC5zdWJsaW5lYWdlLmhiYXIuaGl2LmNvbWJpLCBwLnN1YmxpbmVhZ2UuaGJhci5BZ2UuY29tYmksIG5yb3c9MSwgcmVsX3dpZHRocz1jKDcsMyw0LDQsNCksIGxhYmVscz1jKCJBIiwgIkIiLCAiQyIsICJEIiwgIkUiKSxsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplLCB2anVzdD0xLCBzY2FsZT0wLjk5KQoKIyBhbmQgYWRkIHRoZSBsZWdlbmRzIG9uIHRvcAojcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwud2l0aC5sZWdlbmRzIDwtIHBsb3RfZ3JpZChQSEUuc3VibGluZWFnZS5jb21iaXBsb3QuMS5sZWdlbmRzLCBwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbCwgbmNvbD0xLCByZWxfaGVpZ2h0cz1jKDEsOSkpCgojIGxlZ2VuZHMgYmVsb3cKcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwud2l0aC5sZWdlbmRzIDwtIHBsb3RfZ3JpZChwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbCwgUEhFLnN1YmxpbmVhZ2UuY29tYmlwbG90LjEubGVnZW5kcywgbmNvbD0xLCByZWxfaGVpZ2h0cz1jKDgsMSkpCgoKcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwud2l0aC5sZWdlbmRzCgpgYGAKClwKXCAKVGhlc2UgcGF0dGVybnMgbG9vayBmYWlybHkgc2ltaWxhciBiZXR3ZWVuIHN1YmxpbmVhZ2VzLCBhbmQgKGFwYXJ0IGZyb20gMSAmIDE0KSB0aGUgZ3JvdXBzIGFyZSB2ZXJ5IHNtYWxsLiBIb3dldmVyLCBzdWJsaW5lYWdlIDE0IGRvZXMgYXBwZWFyIHRvIGhhdmUgYSBoaWdoZXIgcHJvcG9ydGlvbiBvZiBNU00gY29tcGFyZWQgdG8gc3VibGluZWFnZSAxIGFuZCBvdGhlcnMuIExldCdzIHRlc3QgdGhhdCBmb3JtYWxseSB1c2luZyAyeDIgZmlzaGVyJ3MgdGVzdHMKXApgYGB7cn0KUEhFLk1TTS5jb3VudHMuYWxsIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShpcy5NU00sIC5kcm9wPUYpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnN1Ymxpbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZSgoaXMuTVNNKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwuc3VibGluLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCgpQSEUuc3VibGluZWFnZS5NU00uY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSxpcy5NU00sIC5kcm9wPUYpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnN1Ymxpbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZSgoaXMuTVNNKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwuc3VibGluLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpICMlPiUKICAjZHBseXI6OmZpbHRlcighaXMubmEoaXMuTVNNKSkKCgpQSEUuc3VibGluZWFnZS5NU00uY291bnRzLndpZGVyIDwtIFBIRS5zdWJsaW5lYWdlLk1TTS5jb3VudHMgJT4lIGRwbHlyOjpzZWxlY3QoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGlzLk1TTSwgQ291bnQpICU+JQogIHRpZHlyOjpwaXZvdF93aWRlcihuYW1lc19mcm9tID0gaXMuTVNNLCB2YWx1ZXNfZnJvbT1Db3VudCkgJT4lCiAgZHBseXI6Om11dGF0ZShNU009cmVwbGFjZV9uYShNU00sIDApLCBPdGhlcj1yZXBsYWNlX25hKE90aGVyLCAwKSwgVG90YWw9c3VtKE1TTSxPdGhlcikpICU+JQogICNkcGx5cjo6c2VsZWN0KC1gTkFgKSAlPiUKICBkcGx5cjo6ZmlsdGVyKFRvdGFsIT0wKQogIAoKUEhFLnN1YmxpbmVhZ2UuTVNNLnB2YWwgPC0gZGF0YS5mcmFtZShUUEEucGluZWNvbmUuc3VibGluZWFnZT1QSEUuc3VibGluZWFnZS5NU00uY291bnRzLndpZGVyJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBwLmZpc2hlcj1zYXBwbHkoMTpucm93KFBIRS5zdWJsaW5lYWdlLk1TTS5jb3VudHMud2lkZXIpLCBmdW5jdGlvbiAoeCkgZmlzaGVyLnRlc3QobWF0cml4KGFzLm51bWVyaWMoYyhQSEUuc3VibGluZWFnZS5NU00uY291bnRzLndpZGVyW3gsIk1TTSJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBIRS5zdWJsaW5lYWdlLk1TTS5jb3VudHMud2lkZXJbeCwiT3RoZXIiXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQSEUuTVNNLmNvdW50cy5hbGxbUEhFLk1TTS5jb3VudHMuYWxsJGlzLk1TTT09Ik1TTSIsIkNvdW50Il0sIFBIRS5NU00uY291bnRzLmFsbFtQSEUuTVNNLmNvdW50cy5hbGwkaXMuTVNNPT0iT3RoZXIiLCJDb3VudCJdKSksbnJvdz0yKSlbWzFdXSksIHN0cmluZ3NBc0ZhY3RvcnM9RikKClBIRS5zdWJsaW5lYWdlLk1TTS5jb3VudHMud2lkZXIgPC0gZHBseXI6OmxlZnRfam9pbihQSEUuc3VibGluZWFnZS5NU00uY291bnRzLndpZGVyLCBQSEUuc3VibGluZWFnZS5NU00ucHZhbCwgYnk9IlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIikKClBIRS5zdWJsaW5lYWdlLk1TTS5jb3VudHMud2lkZXIKYGBgCgoKXApcCiMjIyBWaXN1YWxpc2F0aW9uIG9mIFVLIGdlbm9taWMgcmVsYXRpb25zaGlwcwpcCk9rLCBsZXQncyBtYWtlIGEgdHJlZSBmb3IgZGlzcGxheWluZyB0aGVzZSByZWxhdGlvbnNoaXBzIHVzaW5nIHRoZSBVSyBkYXRhc2V0IG9ubHkKXApGcm9tIHNvbWUgZXhwZXJpbWVudGF0aW9uLCBhICdHcmFwZVRyZWUnIG1pbmltdW0gc3Bhbm5pbmcgbmV0d29yayB3b3JrcyB3ZWxsIGZvciB2aXN1YWxpc2luZyB0aGUgY2xvbmFsaXR5IG9mIHRoZXNlIHBvcHVsYXRpb25zLiBXZSBjYW4gdXNlIGEgU05QLXNjYWxlZCBwaHlsb2dlbnkgYXMgZGlyZWN0IGlucHV0IHRvIEdyYXBlVHJlZSwgYW5kIHRoaXMgd2lsbCBhbGxvdyBicmFuY2hlcyB0byBiZSBzY2FsZWQgYXBwcm9wcmlhdGVseS4gSG93ZXZlciwgYWx0aG91Z2ggYW5ub3RhdGlvbiBpcyBhbGxvd2VkIHdpdGhpbiB0aGUgR3JhcGVUcmVlIHNvZnR3YXJlLCBjb2xvdXJzIG11c3QgYmUgbWFudWFsbHkgZWRpdGVkLiBGaW5hbCBHcmFwZVRyZWUgcGxvdHMgY2FuIHRoZW4gYmUgaW1wb3J0ZWQgYmFjayBpbnRvIFIgZm9yIGNvbWJpbmluZyB3aXRoIG90aGVyIHBsb3RzLiAKXAoKQWx0ZXJuYXRpdmUgdmlzdWFsaXNhdGlvbnMgLSBncmFwZXRyZWU/ClwKVGFrZSB0aGUgNTI2LWdsb2JhbCBwaHlsb2dlbnkgKHNucC1zY2FsZWQgdmVyc2lvbiBmcm9tIHB5amFyKSwgYW5kIHBydW5lIHRvIG9ubHkgaW5jbHVkZSB0aGUgVUsgc3RyYWlucyBmcm9tIHRoaXMgc3R1ZHkgLSB0aGlzIGVuc3VyZXMgdGhlIHRvcG9sb2d5IGlzIGNvbnNpc3RlbnQgYWNjcm9zcyBzdHVkaWVzLiAKYGBge3J9CgpUUEEucHlqYXIudHJlZS5zdWJzZXQudWsgPC0gYXBlOjprZWVwLnRpcChUUEEucHlqYXIudHJlZSwgYXMuY2hhcmFjdGVyKHVubGlzdChQSEUubWV0YWRhdGEubGlua2VkW1BIRS5tZXRhZGF0YS5saW5rZWQkR2VvX0NvdW50cnk9PSJVSyIsIlNhbXBsZV9OYW1lIl0pKSkKCmdndHJlZShUUEEucHlqYXIudHJlZS5zdWJzZXQudWspCiN3cml0ZS50cmVlKFRQQS5weWphci50cmVlLnN1YnNldC51aywgcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEuVUstb25seS5weWphci4yMDIyLTAyLTAzLnRyZSIpKQoKIyBXcml0ZSBvdXQgYSBtZXRhZGF0YSBzaGVldCBmb3IgdGhlIHJlbGV2YW50IGluZm9ybWF0aW9uClBIRS5tZXRhZGF0YS5saW5rZWQuZ3JhcGV0cmVlIDwtIFBIRS5tZXRhZGF0YS5saW5rZWRbLGMoIlNhbXBsZV9OYW1lIiwgInllYXIiLCJnZW5kZXJfb3JpZW50YXRpb24iLCJwaGVfY2VudHJlIiwiaGl2cG9zIiwidWtib3JuIiwiVFBBX0xpbmVhZ2UiLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIpXQpjb2xuYW1lcyhQSEUubWV0YWRhdGEubGlua2VkLmdyYXBldHJlZSlbMV0gPC0gIklEIgoKI3dyaXRlLnRhYmxlKFBIRS5tZXRhZGF0YS5saW5rZWQuZ3JhcGV0cmVlLCBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS5VSy1vbmx5LmdyYXBldHJlZS5tZXRhLjIwMjItMDItMDMudHN2IiksIHNlcCA9ICJcdCIsIHF1b3RlPUYsIHJvdy5uYW1lcyA9IEYpCmBgYApcClRyZWUgaW5kZXBlbmRlbnRseSB2aXN1YWxpc2VkIGFuZCBhbm5vdGF0ZWQgdXNpbmcgR3JhcGVUcmVlLgpcCk5vdyBpbXBvcnQgYW5kIGludGVncmF0ZSBHcmFwZVRyZWUgcGxvdCB3aXRoIG1ldGFkYXRhIHBsb3RzLgpgYGB7ciwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9CiMgQ29tYmluZSB0aGUgcGxvdHMKcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwuQjJGIDwtIHBsb3RfZ3JpZChwLnN1YmxpbmVhZ2UueWVhci5idWJibGVwbG90LmNvbWJpLCBwLnN1YmxpbmVhZ2UuaGJhci5jb3VudHMuY29tYmksIHAuc3VibGluZWFnZS5oYmFyLm9yaWVudGF0aW9uLmNvbWJpLCBwLnN1YmxpbmVhZ2UuaGJhci5oaXYuY29tYmksIHAuc3VibGluZWFnZS5oYmFyLkFnZS5jb21iaSwgbnJvdz0xLCByZWxfd2lkdGhzPWMoNyw0LDQsNCw0KSwgbGFiZWxzPWMoIkIiLCAiQyIsICJEIiwgIkUiLCAiRiIpLGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUsIHZqdXN0PTEsIHNjYWxlPTAuOTcpCgojIGxlZ2VuZHMgYmVsb3cKcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwud2l0aC5sZWdlbmRzLkIyRiA8LSBwbG90X2dyaWQocC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwuQjJGLCBQSEUuc3VibGluZWFnZS5jb21iaXBsb3QuMS5sZWdlbmRzLCBuY29sPTEsIHJlbF9oZWlnaHRzPWMoNywxKSkKCiNwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbC53aXRoLmxlZ2VuZHMuQjJGCgoKIyBOb3cgYnJpbmcgaW4gZXh0ZXJuYWxseSBwbG90dGVkIEdyYXBldHJlZQpwLlRQQS5VSy5HcmFwZXRyZWUuc3VibGluZWFnZXMgPC0gZ2dkcmF3KCkgKyBkcmF3X2ltYWdlKFRQQS5VSy5HcmFwZXRyZWUuc3VibGluZWFnZXMuZmlsZSkKcC5UUEEuVUsuR3JhcGV0cmVlLnN1YmxpbmVhZ2VzCgpwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbC53aXRoLmxlZ2VuZHMuQjJGLndpdGguZ3JhcGV0cmVlIDwtIHBsb3RfZ3JpZChwLlRQQS5VSy5HcmFwZXRyZWUuc3VibGluZWFnZXMsIHAuc3VibGluZWFnZS5oYmFyLmNvbWJpLnBsdXMuYWxsLndpdGgubGVnZW5kcy5CMkYsIG5jb2w9MSwgbGFiZWxzPWMoIkEiLCIiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgcmVsX2hlaWdodHM9YygzLDUpKSAKCgpwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbC53aXRoLmxlZ2VuZHMuQjJGLndpdGguZ3JhcGV0cmVlCiNnZ3NhdmUocGFzdGUwKEZpZ3VyZV9vdXRwdXRfZGlyZWN0b3J5LCAiRmlnMV9UUEEtUEhFX1NhbXBsZS1kaXN0cm9zLXN1YmxpbmVhZ2UuIixmb3JtYXQoU3lzLkRhdGUoKSwiJVklbSVkIiksIi5wZGYiKSwgdW5pdHM9J21tJywgd2lkdGg9MTkwLCBoZWlnaHQ9MTg1LCBkZXZpY2U9J3BkZicsIGRwaT0xMjAwKQpgYGAKClwKTWFuYWdlIG90aGVyIEdyYXBlVHJlZSBwbG90cyAoZm9yIGNvbnNpc3RlbmN5KQoKVFBBLVVLLTIwMjItMDItMTYuLU1TVHJlZV8zLXdheS1maWd1cmUuSW5zY2FwZWQtMgpgYGB7cn0KIyBCcmluZyBpbiAzLXdheSBncmFwaGV0cmVlIHBsb3QgKDMgZGlmZmVyZW50IG1ldGFkYXRhIHZhcmlhYmxlcyB1c2luZyB0aGUgc2FtZSBpbnB1dCB0cmVlKQpUUEEuVUsuR3JhcGV0cmVlLjN3YXkgPC0gZ2dkcmF3KCkgKyBkcmF3X2ltYWdlKFRQQS5VSy5HcmFwZXRyZWUuM3dheS5maWxlKQpUUEEuVUsuR3JhcGV0cmVlLjN3YXkKCiNnZ3NhdmUocGFzdGUwKEZpZ3VyZV9vdXRwdXRfZGlyZWN0b3J5LCAiU3VwRmlnNF9UUEEtUEhFX0dyYXBldHJlZS0zd2F5cy4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xNDUsIGhlaWdodD0xODAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCgpgYGAKClwKQW5kIGFsc28gZG8gdGhlIEhJViBzdGF0dXMgcGxvdApgYGB7cn0KClRQQS5VSy5HcmFwZXRyZWUuSElWIDwtIGdnZHJhdygpICsgZHJhd19pbWFnZShUUEEuVUsuR3JhcGV0cmVlLkhJVi5maWxlKQpUUEEuVUsuR3JhcGV0cmVlLkhJVgoKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksICJTdXBGaWc1X1RQQS1QSEVfR3JhcGV0cmVlLUhJVi4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xODUsIGhlaWdodD0xMTAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCgpgYGAKCgoKXApcCiMjIyBQaHlsb2dlbmV0aWMgY29udGV4dCBhbmFseXNlcwpcCk9rLCBub3cgbGV0cyBsb29rIGF0IHNvbWUgdHJlZXMKXApGaXJzdCwgbGV0J3MgZm9ybWFsaXNlIEJFQVNUIHRyZWUgcGxvdHRpbmcgYXMgdGhyZWUgc2VwYXJhdGUgZnVuY3Rpb25zIHRvIGVuYWJsZSBvdGhlciB0cmVlcyB0byBiZSBwbG90dGVkIHRoZSBzYW1lIHdheQpcCmBgYHtyfQpmdWxsLmJlYXN0Mi50cmVlIDwtIHJlYWQuYmVhc3QoZnVsbC5iZWFzdDIudHJlZS5maWxlKQpmdWxsLmJlYXN0Mi50cmVlQHBoeWxvJHRpcC5sYWJlbCA8LSBnc3ViKCJcXHwuKyQiLCIiLGZ1bGwuYmVhc3QyLnRyZWVAcGh5bG8kdGlwLmxhYmVsLCBwZXJsPVQpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBmdW5jdGlvbiB0byBleHRyYWN0IGEgdHJlZSBiYXNlZCBvbiBzdWJsaW5lYWdlCkV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90IDwtIGZ1bmN0aW9uKG15LmJlYXN0LnRyZWUsIG15Lm1ldGFkYXRhLCBteS5waGUubWV0YSwgbXkuc3VibGluZWFnZSl7CiAgIyBnZXQgYWxsIHRpcHMgdG8gaW5jbHVkZSBmcm9tIG1ldGFkYXRhLCB0aGVuIGNhbGN1bGF0ZSBNUkNBIGZyb20gdHJlZQogIHN1YmxpbmVhZ2UudGVzdC5tcmNhIDwtIGdldE1SQ0EobXkuYmVhc3QudHJlZUBwaHlsbywgYXMuY2hhcmFjdGVyKHVubGlzdChteS5tZXRhZGF0YVtteS5tZXRhZGF0YSRUUEEucGluZWNvbmUuc3VibGluZWFnZT09bXkuc3VibGluZWFnZSwiU2FtcGxlX05hbWUiXSkpKQogICMjIyMjIwogIFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QgPC0gdHJlZV9zdWJzZXQobXkuYmVhc3QudHJlZSwgbm9kZT1zdWJsaW5lYWdlLnRlc3QubXJjYSwgbGV2ZWxzX2JhY2s9MCkKICByZXR1cm4oVFBBLmJlYXN0LnN1YnRyZWUudGVzdCkKfQojRXh0cmFjdF9zdWJsaW5lYWdlX3RyZWVfZm9yX3Bsb3QoZnVsbC5iZWFzdDIudHJlZSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIG15LnN1YmxpbmVhZ2UgPSAxKQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBGdW5jdGlvbiB0byBwcmVwYXJlIGEgYmVhc3QgdHJlZSB3aXRoIHRpbWVzY2FsZSBpbmRpY2F0b3JzLCBwb3N0ZXJpb3Igc3VwcG9ydCBhbmQgOTUlIEhQRCBiYXJzCnBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX0hQRCA8LSBmdW5jdGlvbihteS5iZWFzdC50cmVlLCBteS5tZXRhZGF0YSwgbXkucGhlLm1ldGEsIG1yc2QuZnVsbHRyZWUpewogICMgZ2V0IE1SQ0QgZm9yIHRyZWUKICBtcnNkLkJlYXN0LnRyZWUudGVzdC5zIDwtIG1heChhcy5udW1lcmljKHVubGlzdChteS5tZXRhZGF0YVtteS5tZXRhZGF0YSRTYW1wbGVfTmFtZSAlaW4lIG15LmJlYXN0LnRyZWVAcGh5bG8kdGlwLmxhYmVsLCJTYW1wbGVfWWVhciJdKSkpCiAgbXJzZC5CZWFzdC50cmVlLnRlc3QgPC0gbHVicmlkYXRlOjp5bWQocGFzdGUwKG1yc2QuQmVhc3QudHJlZS50ZXN0LnMsIi0wNi0wMSIpKSAKICBtcnNkLkJlYXN0LnRyZWUuZnVsbHRyZWUgPC0gbHVicmlkYXRlOjp5bWQobXJzZC5mdWxsdHJlZSkgCiAgI21yc2QuQmVhc3QudHJlZS50ZXN0CiAgIyBwbG90IGJhc2ljIHRyZWUKICBvcHRpb25zKGlnbm9yZS5uZWdhdGl2ZS5lZGdlPVRSVUUpCiAgcC5UUEEuYmVhc3Quc3VidHJlZS50ZXN0IDwtIGdndHJlZShteS5iZWFzdC50cmVlLCBtcnNkPW1yc2QuQmVhc3QudHJlZS50ZXN0LCBsYWRkZXJpemUgPSBULCBzaXplPTAuNCkgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgxOTYwLDIwMjAsMTApLCBtaW5vcl9icmVha3M9c2VxKDIwMDAsIDIwMjAsIDEpKSArCiAgICB0aGVtZV90cmVlMigpICsKICAgICMgQWRkIGRhdGUgbGluZXMgZm9yIGVhc3kgaW50ZXJwcmV0YXRpb24gIAogICAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciAgID0gZWxlbWVudF9saW5lKGNvbG9yPSJncmV5NTAiLCBzaXplPS4yKSwKICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgICA9IGVsZW1lbnRfbGluZShjb2xvcj0iZ3JleTg1Iiwgc2l6ZT0uMiksCiAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCkpCiAgIyBBZGQgcG9zdGVyaW9yIHN1cHBvcnQgYXMgbm9kZSBwb2ludHMKICBwLlRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QgPC0gcC5UUEEuYmVhc3Quc3VidHJlZS50ZXN0ICsgZ2VvbV9wb2ludDIoYWVzKHN1YnNldD0oIWlzVGlwICYgYXMubnVtZXJpYyhwb3N0ZXJpb3IpPjAuOCkpLGNvbG9yPSJncmF5NjAiLHNpemU9MixhbHBoYT0wLjUsIHNoYXBlPTE4KSArIAogICAgZ2VvbV9wb2ludDIoYWVzKHN1YnNldD0oIWlzVGlwICYgYXMubnVtZXJpYyhwb3N0ZXJpb3IpPjAuOTEpKSxjb2xvcj0iZ3JheTQwIixzaXplPTMsc2hhcGU9MTgsYWxwaGE9MC41KSArIAogICAgZ2VvbV9wb2ludDIoYWVzKHN1YnNldD0oIWlzVGlwICYgYXMubnVtZXJpYyhwb3N0ZXJpb3IpPj0wLjk2KSksY29sb3I9ImJsYWNrIixzaXplPTMsc2hhcGU9MTgsYWxwaGE9MC41KQogICMjIyMjIwogICMgZXh0cmFjdCA5NSUgSFBEIGludGVydmFscyAtIGdlb21fcmFuZ2Ugc2VlbXMgdW5hYmxlIHRvIGRvIGNvcnJlY3RseSB3aXRoIHRoaXMgdHJlZSAoa25vd24gYnVnIGZvciB0aXAgZGF0ZWQgdHJlZXMpLCBzbyBleHRyYWN0IGRhdGEgYW5kIHBsb3QgdXNpbmcgZ2VvbV9zZWdtZW50CiAgVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5kYXRhIDwtIGZvcnRpZnkobXkuYmVhc3QudHJlZSkKICBtaW5tYXggPC0gdChtYXRyaXgodW5saXN0KFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZGF0YVshaXMubmEoVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5kYXRhJGhlaWdodF8wLjk1X0hQRCksImhlaWdodF8wLjk1X0hQRCJdKSxucm93PTIpKQogIGJhcl9kZiA8LSBkYXRhLmZyYW1lKG5vZGVfaWQ9VFBBLmJlYXN0LnN1YnRyZWUudGVzdC5kYXRhWyFpcy5uYShUUEEuYmVhc3Quc3VidHJlZS50ZXN0LmRhdGEkaGVpZ2h0XzAuOTVfSFBEKSwibm9kZSJdLGFzLmRhdGEuZnJhbWUobWlubWF4KSkKICBuYW1lcyhiYXJfZGYpIDwtIGMoJ25vZGVfaWQnLCdtaW4nLCdtYXgnKSAKICBiYXJfZGYgPC0gYmFyX2RmICU+JSBmaWx0ZXIobm9kZV9pZCA+IE50aXAobXkuYmVhc3QudHJlZUBwaHlsbykpCiAgYmFyX2RmIDwtIGJhcl9kZiAlPiUgbGVmdF9qb2luKFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZGF0YSwgYnk9Yygnbm9kZV9pZCc9J25vZGUnKSkgIyU+JSBzZWxlY3Qobm9kZV9pZCxtaW4sbWF4LHkpCiAgI21yY2QuZGVjaW1hbCA8LSBkZWNpbWFsX2RhdGUobXJzZC5CZWFzdC50cmVlLnRlc3QpCiAgbXJjZC5kZWNpbWFsIDwtIGRlY2ltYWxfZGF0ZShtcnNkLkJlYXN0LnRyZWUuZnVsbHRyZWUpCiAgCiAgIyBOb3cgYWRkIEhQRHMgdG8gcGxvdAogIHAuVFBBLmJlYXN0LnN1YnRyZWUudGVzdCA8LSBwLlRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QgKyBnZW9tX3NlZ21lbnQoYWVzKHg9bXJjZC5kZWNpbWFsLW1heCwgeT15LCB4ZW5kPW1yY2QuZGVjaW1hbC1taW4sIHllbmQ9eSksIGRhdGE9YmFyX2RmLCBjb2xvcj0ncmVkJywgYWxwaGE9MC4yLCBzaXplPTIuMCkKICAjIE91dHB1dCB0cmVlIAogIHJldHVybihwLlRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QpCn0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgRnVuY3Rpb24gdG8gYWRkIG1ldGFkYXRhIHRvIHRyZWUKIyBIYXMgdHdvIG9wdGlvbmFsIGFyZ3VtZW50cyAiaW5pdGlhbC50cmFjay5vZmZzZXQiIGFuZCAidHJhY2suc2NhbGluZyIgd2hpY2ggY2FuIGJlIHVzZWQgdG8gYWx0ZXIgdGhlIHdpZHRoIGFuZCBwb3NpdGlvbmluZyBvZiBtZXRhZGF0YSB0cmFja3MKCnBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX1BIRV9tZXRhZGF0YSA8LSBmdW5jdGlvbihteS5iZWFzdC50cmVlLmlucHV0LCBteS5tZXRhZGF0YSwgbXkucGhlLm1ldGEsIGluaXRpYWwudHJhY2sub2Zmc2V0LCB0cmFjay5zY2FsaW5nKXsKICAgICMgQWRkIGNvZGUgdG8gYWxsb3cgc2NhbGluZyB1cCBvZiB0aGUgdHJhY2sgb2Zmc2V0cyBhbmQgd2lkdGhzIC0gdXNlZnVsIGZvciBtdWNoIGJpZ2dlciBsZW5ndGggdHJlZXMKICBpZihtaXNzaW5nKGluaXRpYWwudHJhY2sub2Zmc2V0KSl7CiAgICBpbml0aWFsLnRyYWNrLm9mZnNldCA8LSAwCiAgfSAgICAKICBpZihtaXNzaW5nKHRyYWNrLnNjYWxpbmcpKXsKICAgIHRyYWNrLnNjYWxpbmcgPC0gMQogIH0KICAjIENhbGN1bGF0ZSBhbW91bnQgdG8gb2Zmc2V0IGVhY2ggaGVhdG1hcCB0cmFjawogIG9mZnNldC5kaXN0IDwtIDQqdHJhY2suc2NhbGluZwogIHRyYWNrLndpZHRoIDwtICgxL21heChteS5iZWFzdC50cmVlLmlucHV0JGRhdGEkaGVpZ2h0KSozKSp0cmFjay5zY2FsaW5nCiAgCiAgIyBtYWtlIGEgbGlzdCBvZiB0YXhhIHVzZWQgaW4gdGhpcyBwbG90IAogIG15LnRheGEubGlzdCA8LSBhcy5jaGFyYWN0ZXIodW5saXN0KGZpbHRlcihteS5iZWFzdC50cmVlLmlucHV0JGRhdGEsIGlzVGlwPT1UUlVFKSAlPiUgc2VsZWN0KGxhYmVsKSkpCiAgCiAgIyBtYWtlIGEgY29sb3Igc2NhbGUgZm9yIHNhbXBsaW5nIHllYXJzCiAgI1BIRS5zdWJsaW50ZXN0LnllYXIuY29scyA8LSBkYXRhLmZyYW1lKHllYXI9c29ydCh1bmlxdWUoYXMubnVtZXJpYyh1bmxpc3QobXkubWV0YWRhdGFbKG15Lm1ldGFkYXRhJFNhbXBsZV9OYW1lICVpbiUgbXkudGF4YS5saXN0KSwiU2FtcGxlX1llYXIiXSx1c2UubmFtZXM9RikpKSksc3RyaW5nc0FzRmFjdG9ycyA9IFQpCiAgI1BIRS5zdWJsaW50ZXN0LnllYXIuY29scyR5ZWFyLmNvbHMgPC0gY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKDcsICJZbE9yUmQiKSkobnJvdyhQSEUuc3VibGludGVzdC55ZWFyLmNvbHMpKQogIAogICMgT3IgYWx0ZXJuYXRpdmVseSwgdXNlIGEgY29tbW9uIGNvbG91ciBzY2hlbWUgZm9yIGFsbCBkYXRhIChtYXliZSBtb3JlIHNlbnNpYmxlKQogIFBIRS5zdWJsaW50ZXN0LnllYXIuY29scyA8LSBkYXRhLmZyYW1lKHllYXI9VFBBLnllYXIuY3V0dG9mZi5jb2xzJGRhdGUuY3V0dG9mZiwgeWVhci5jb2xzPVRQQS55ZWFyLmN1dHRvZmYuY29scyRkYXRlLmN1dHRvZmYuY29sLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKICAKICAjIG1ha2UgbWV0YWRhdGEgZmlsZSBmb3IgVUsgcmVnaW9ucyBwcmVzZW50IGluIHN1YmxpbmVhZ2UKICBzdWJsaW4udGVzdC5yZWdpb24ubWV0YSA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcz1hcy5jaGFyYWN0ZXIodW5saXN0KG15LnBoZS5tZXRhW215LnBoZS5tZXRhJFNhbXBsZV9OYW1lICVpbiUgbXkudGF4YS5saXN0LCJTYW1wbGVfTmFtZSJdKSksIFJlZ2lvbj1hcy5jaGFyYWN0ZXIodW5saXN0KG15LnBoZS5tZXRhW215LnBoZS5tZXRhJFNhbXBsZV9OYW1lICVpbiUgbXkudGF4YS5saXN0LCJwaGVfY2VudHJlIl0pKSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgCiAgIyBBZGQgaGVhdG1hcCBzdHJpcHMKICAjIFNhbXBsZSBZZWFyCiAgI1RQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZ2xvYmFsLnBsb3QxLnJlZ2lvbmFsIDwtIGdoZWF0bWFwKG15LmJlYXN0LnRyZWUuaW5wdXQsIFRQQS5yYXdzZXEuYWxsLlllYXJzLnAsIGNvbG9yPU5VTEwsd2lkdGg9dHJhY2sud2lkdGgsIG9mZnNldD1pbml0aWFsLnRyYWNrLm9mZnNldCtvZmZzZXQuZGlzdCxjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCwgZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsKICAgICNzY2FsZV9maWxsX21hbnVhbChuYW1lPSJZZWFyIiwgdmFsdWVzPVBIRS5zdWJsaW50ZXN0LnllYXIuY29scyR5ZWFyLmNvbHMsYnJlYWtzPVBIRS5zdWJsaW50ZXN0LnllYXIuY29scyR5ZWFyLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEsIG5jb2w9MikpICsKICAgICNnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCiAgVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgPC0gZ2hlYXRtYXAobXkuYmVhc3QudHJlZS5pbnB1dCwgVFBBLnJhd3NlcS55ZWFyLmN1dHRvZmYucCwgY29sb3I9TlVMTCx3aWR0aD10cmFjay53aWR0aCwgb2Zmc2V0PWluaXRpYWwudHJhY2sub2Zmc2V0K29mZnNldC5kaXN0LGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iWWVhciIsIHZhbHVlcz1QSEUuc3VibGludGVzdC55ZWFyLmNvbHMkeWVhci5jb2xzLGJyZWFrcz1QSEUuc3VibGludGVzdC55ZWFyLmNvbHMkeWVhciwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxLCBuY29sPTIpKSArCiAgICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCiAgCiAgIyBBZGQgY291bnRyeQogIFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZ2xvYmFsLnBsb3QxLnJlZ2lvbmFsIDwtIGdoZWF0bWFwKFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZ2xvYmFsLnBsb3QxLnJlZ2lvbmFsLCBUUEEucmF3c2VxLmNvdW50cmllcy5wLCBjb2xvcj1OVUxMLHdpZHRoPXRyYWNrLndpZHRoLCBvZmZzZXQ9aW5pdGlhbC50cmFjay5vZmZzZXQrKG9mZnNldC5kaXN0KjIpLGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkNvdW50cnkiLCB2YWx1ZXM9Y29udGluZW50YWwuY291bnRyeS5jb2xzLmJyZXcyJGNvdW50cnkuY29sLCBicmVha3M9Y29udGluZW50YWwuY291bnRyeS5jb2xzLmJyZXcyJEdlb19Db3VudHJ5LCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKSArCiAgICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCiAgIyBVSyBvciBub24tVUsKICBUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCA8LSBnaGVhdG1hcChUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUEEucmF3c2VxLlVLLnAsIGNvbG9yPU5VTEwsd2lkdGg9dHJhY2sud2lkdGgsb2Zmc2V0PWluaXRpYWwudHJhY2sub2Zmc2V0KyhvZmZzZXQuZGlzdCozKSwgY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJFbmdsYW5kL090aGVyIiwgYnJlYWtzPWMoIkVuZ2xhbmQiLCJPdGhlciIpLCB2YWx1ZXM9YygiYmxhY2siLCJncmV5OTUiKSwgbmEudmFsdWUgPSAid2hpdGUiLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDMsIG5jb2w9MikpICsKICAgIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKICAjIFVLIFBIRSByZWdpb24KICBUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCA8LSBnaGVhdG1hcChUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCwgc3VibGluLnRlc3QucmVnaW9uLm1ldGEsIGNvbG9yPU5VTEwsd2lkdGg9dHJhY2sud2lkdGgsIG9mZnNldD1pbml0aWFsLnRyYWNrLm9mZnNldCsob2Zmc2V0LmRpc3QqNCksY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsIGZvbnQuc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluKSArIAogICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iVUtIU0EgUmVnaW9uIiwgdmFsdWVzPVBIRS5yZWdpb24uY29scy5icmV3JHJlZ2lvbi5jb2wsIGJyZWFrcz1QSEUucmVnaW9uLmNvbHMuYnJldyRVS0hTQS5yZWdpb24sIG5hLnZhbHVlID0gIndoaXRlIiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSA0KSkgKwogICAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2ZpbGwoKQogIAogICMgVFBBIHN1YmxpbmVhZ2UKICAjVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgPC0gZ2hlYXRtYXAoVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBTdWJsaW5lYWdlPVRQQS5tZXRhMi4xJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBzdHJpbmdzQXNGYWN0b3JzID0gRiksIGNvbG9yPU5VTEwsd2lkdGg9dHJhY2sud2lkdGgsb2Zmc2V0PWluaXRpYWwudHJhY2sub2Zmc2V0KyhvZmZzZXQuZGlzdCo1KSwgY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsZm9udC5zaXplPTIuNSkgKyAKICAjc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iU3VibGluZWFnZSIsdmFsdWVzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMsIGJyZWFrcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSA1KSkgCiAgCiAgVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgPC0gVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgKyB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogICAgbmV3X3NjYWxlX2ZpbGwoKSArCiAgICBnZW9tX3Jvb3RlZGdlKDIpICsKICAgIE5VTEwKICAKICAjIGNhbGN1bGF0ZSBudW1iZXIgb2YgdGF4YQogIHRlc3QudGF4YWNvdW50IDwtIGxlbmd0aChteS50YXhhLmxpc3QpCiAgIyBBZGp1c3QgZmluYWwgcGxvdCB4IGFuZCB5IGF4aXMgdG8gbWFrZSBzcGFjZSBmb3IgbGFiZWxzIHVzaW5nIHRheGEgY291bnRzCiAgeC5heGlzLmxpbWl0cyA8LSBnZ3Bsb3RfYnVpbGQoVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwpJGxheW91dCRwYW5lbF9zY2FsZXNfeFtbMV1dJHJhbmdlJHJhbmdlCiAgVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgPC0gVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgKyAKICAgIGNvb3JkX2NhcnRlc2lhbih5PWMoLTAuNS0odGVzdC50YXhhY291bnQvMTUpLHRlc3QudGF4YWNvdW50KzIpLCB4PWMoeC5heGlzLmxpbWl0c1sxXSx4LmF4aXMubGltaXRzWzJdKzMpKQogIAogIHJldHVybihUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCkKfQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCmBgYAoKXApHcmVhdCwgbm93IGxldCdzIHBsb3QgYSBmdWxsIGJlYXN0IHRyZWUKYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0KIyBmdW5jdGlvbiBmb3IgeC1heGlzIHRpbWUgYnJlYWtzIG5lZWRzIHR3ZWFraW5nIGZvciB0aGUgZnVsbCB0cmVlClRQQS5HbG9iYWwuZnVsbC5CZWFzdFRyZWUudWttZXRhIDwtIHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX1BIRV9tZXRhZGF0YShwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9IUEQobXkuYmVhc3QudHJlZSA9IGZ1bGwuYmVhc3QyLnRyZWUsIG15Lm1ldGFkYXRhID0gVFBBLm1ldGEyLjEsIG15LnBoZS5tZXRhID0gUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXJzZC5mdWxsdHJlZSA9ICIyMDE5LTA2LTAxIikgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgxNDAwLDIwMjAsNTApLCBtaW5vcl9icmVha3M9c2VxKDE5NTAsIDIwMjAsIDUpKSwgbXkubWV0YWRhdGEgPSBUUEEubWV0YTIuMSwgbXkucGhlLm1ldGEgPSBQSEUubWV0YWRhdGEubGlua2VkLCB0cmFjay5zY2FsaW5nID0gNSkKClRQQS5HbG9iYWwuZnVsbC5CZWFzdFRyZWUudWttZXRhCgojZ2dzYXZlKHBhc3RlMChGaWd1cmVfb3V0cHV0X2RpcmVjdG9yeSwiU3VwRmlnN19UUEFfRnVsbEJlYXN0VHJlZS4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xODUsIGhlaWdodD0yNDAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCmBgYAoKXApOb3cgZG8gc3VibGluZWFnZSBwbG90cwpcCk1ha2Ugc29tZSBwbG90cwpgYGB7ciwgd2FybmluZz1GQUxTRX0KIyBTdWJsaW5lYWdlIDEKc3VibGluZWFnZS4xLnRyZWUuaGVhdG1hcCA8LSBwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9QSEVfbWV0YWRhdGEocGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfSFBEKEV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90KGZ1bGwuYmVhc3QyLnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBteS5zdWJsaW5lYWdlID0gMSksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCIyMDE5LTA2LTAxIiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCB0cmFjay5zY2FsaW5nID0gMS4yKQoKIyBTdWJsaW5lYWdlLjIKc3VibGluZWFnZS4yLnRyZWUuaGVhdG1hcCA8LSBwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9QSEVfbWV0YWRhdGEocGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfSFBEKEV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90KGZ1bGwuYmVhc3QyLnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBteS5zdWJsaW5lYWdlID0gMiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCIyMDE5LTA2LTAxIiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCB0cmFjay5zY2FsaW5nID0gMSkKCiMgU3VibGluZWFnZS44CnN1YmxpbmVhZ2UuOC50cmVlLmhlYXRtYXAgPC0gcGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfUEhFX21ldGFkYXRhKHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX0hQRChFeHRyYWN0X3N1YmxpbmVhZ2VfdHJlZV9mb3JfcGxvdChmdWxsLmJlYXN0Mi50cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXkuc3VibGluZWFnZSA9IDgpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwiMjAxOS0wNi0wMSIpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgdHJhY2suc2NhbGluZyA9IDEuMSkKCiMgU3VibGluZWFnZS4xNApzdWJsaW5lYWdlLjE0LnRyZWUuaGVhdG1hcCA8LSBwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9QSEVfbWV0YWRhdGEocGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfSFBEKEV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90KGZ1bGwuYmVhc3QyLnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBteS5zdWJsaW5lYWdlID0gMTQpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwiMjAxOS0wNi0wMSIpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgdHJhY2suc2NhbGluZyA9IDEuMSkKCmBgYAoKXApQbG90IHRvZ2V0aGVyPwpcCk1heWJlIHdpdGggc3VibGluZWFnZSAxIGV4cGFuZGVkPwpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEyfQpwLmJlYXN0LnRyZWVzLmhlYXRtYXAuc3VibGluZWFnZXMuY29tYmkub2Zmc2V0MSA8LSBwbG90X2dyaWQoc3VibGluZWFnZS4yLnRyZWUuaGVhdG1hcCwgCiAgICAgICAgICBzdWJsaW5lYWdlLjgudHJlZS5oZWF0bWFwLCAKICAgICAgICAgIHN1YmxpbmVhZ2UuMTQudHJlZS5oZWF0bWFwLCAKICAgICAgICAgIG5jb2w9MiwgbGFiZWxzPWMoIkIgLSBTdWJsaW5lYWdlIDIiLCJDIC0gU3VibGluZWFnZSA4IiwiRCAtIFN1YmxpbmVhZ2UgMTQiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgc2NhbGU9MC45NSwgdmp1c3Q9MS4wKQoKcC5iZWFzdC50cmVlcy5oZWF0bWFwLnN1YmxpbmVhZ2VzLmNvbWJpLm9mZnNldDIgPC0gcGxvdF9ncmlkKHN1YmxpbmVhZ2UuMS50cmVlLmhlYXRtYXAsIHAuYmVhc3QudHJlZXMuaGVhdG1hcC5zdWJsaW5lYWdlcy5jb21iaS5vZmZzZXQxLCBsYWJlbHM9YygiQSAtIFN1YmxpbmVhZ2UgMSIsICIiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgc2NhbGU9MC45NzUsIG5jb2w9MiwgcmVsX3dpZHRocz1jKDYsMTEpLCB2anVzdD0yLjUpCgoKcC5iZWFzdC50cmVlcy5oZWF0bWFwLnN1YmxpbmVhZ2VzLmNvbWJpLm9mZnNldDIKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksIlN1cEZpZzhfVFBBLVBIRV9TdWJsaW5lYWdlLUJlYXN0VHJlZXMuIixmb3JtYXQoU3lzLkRhdGUoKSwiJVklbSVkIiksIi5wZGYiKSwgdW5pdHM9J21tJywgd2lkdGg9MjY1LCBoZWlnaHQ9MjMwLCBkZXZpY2U9J3BkZicsIGRwaT0xMjAwKQpgYGAKClwKTmVlZCB0byBleHBsb3JlIHN1YmxpbmVhZ2UgMTQgYSBiaXQgbW9yZSB0byBnZXQgZGF0ZXMgZm9yIHRob3NlIHN1YmNsYWRlcwpgYGB7cn0Kc3VibGluZWFnZS4xNC50cmVlLmhlYXRtYXAgKyBnZW9tX3RpcGxhYihzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGxpbmVzaXplPTAuNCkgIzMKYGBgCgpcCmBgYHtyfQojIE9rLCB0aGVyZSBhcmUgbXVsdGlwbGUgc3ViY2xhZGVzIGluIHRoaXMgdHJlZQpzdWJsaW5lYWdlLjE0LnRyZWUuaGVhdG1hcC5kYXRhIDwtIHN1YmxpbmVhZ2UuMTQudHJlZS5oZWF0bWFwJGRhdGEKCiMgZ2V0TVJDQShmdWxsLmJlYXN0Mi50cmVlQHBoeWxvLGMoIlBIRTE1MDE1MEEiLCJOTDE0IiwiVFBBX0JDQzEyMiIsIlRQQV9CQ0MxMjYiLCJQSEUxNDAwNzZBIiwiVFBBX1VLQlJHMDA4IikpICA5ODIKIyBmdWxsLmJlYXN0Mi50cmVlQHBoeWxvJHRpcC5sYWJlbFtwaGFuZ29ybjo6RGVzY2VuZGFudHMoZnVsbC5iZWFzdDIudHJlZUBwaHlsbywgOTgyLCB0eXBlID0gYygidGlwcyIpKVtbMV1dXQoKc3VibGluZWFnZS4xNC5sb3dlcmNsYWRlLmxpc3QgPC0gYygiTkwxNyIsICJOTDE5IiwgIlBIRTE0MDA4NUEiLCAiUEhFMTQwMDg5QSIsICJQSEUxNTAxMThBIiwgIlBIRTE1MDEyMUEiLCAiUEhFMTUwMTMzQSIsICJQSEUxNTAxNDNBIiwgIlBIRTE1MDE0NUEiLCAiUEhFMTUwMTYyQSIsICJQSEUxNTAxNjZBIiwgIlBIRTE1MDE2OEEiLCAiUEhFMTYwMjI0QSIsICJQSEUxNjAyNDNBIiwgIlBIRTE2MDI1NUEiLCAiUEhFMTYwMjc2QSIsICJQSEUxNjAyOTBBIiwgIlBIRTE2MDMwMkEiLCAiUEhFMTYwMzA2QSIsICJQSEUxNzAzMzNBIiwgIlBIRTE3MDM0OUEiLCAiUEhFMTcwMzc0QSIsICJQSEUxNzAzODFBIiwgIlBIRTE3MDY2NEEiLCAiVFBBX0VTQkNOMDA1IiwgIlRQQV9VS0JJUjAzMiIpCgpzdWJsaW5lYWdlLjE0LnVwcGVyY2xhZGUubGlzdCA8LSBjKCJOTDE0IiwgIlBIRTE0MDA3NkEiLCAiUEhFMTUwMTQ5QSIsICJQSEUxNTAxNTBBIiwgIlBIRTE1MDE3MEEiLCAiUEhFMTYwMTk2QSIsICJQSEUxNjAyNjNBIiwgIlBIRTE2MDI3NEEiLCAiUEhFMTYwMjg3QSIsICJQSEUxNjAyOTRBIiwgIlBIRTE2MDMxNkEiLCAiUEhFMTYwMzE3QSIsICJQSEUxNzAzNzJBIiwgIlBIRTE3MDM4NkEiLCAiUEhFMTcwMzk3QSIsICJQSEUxNzA0MDVBIiwgIlRQQV9CQ0MwODEiLCAiVFBBX0JDQzA4OCIsICJUUEFfQkNDMDg5IiwgIlRQQV9CQ0MxMDEiLCAiVFBBX0JDQzEyMiIsICJUUEFfQkNDMTI2IiwgIlRQQV9CQ0MxMzYiLCAiVFBBX0JDQzE2OSIsICJUUEFfSFVOMTgwMDA0IiwgIlRQQV9IVU4xOTAwMjAiLCAiVFBBX1VLQklSMDQ0IiwgIlRQQV9VS0JSRzAwNyIsICJUUEFfVUtCUkcwMDgiKQoKIyBHZXQgTVJDQSBkYXRlIGZvciBsb3dlciBjbGFkZQpzdWJsaW5lYWdlLjE0Lmxvd2VyY2xhZGUubGlzdC50bXJjYSA8LSBzdWJsaW5lYWdlLjE0LnRyZWUuaGVhdG1hcC5kYXRhW3N1YmxpbmVhZ2UuMTQudHJlZS5oZWF0bWFwLmRhdGEkbm9kZT09Z2V0TVJDQShFeHRyYWN0X3N1YmxpbmVhZ2VfdHJlZV9mb3JfcGxvdChmdWxsLmJlYXN0Mi50cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXkuc3VibGluZWFnZSA9IDE0KUBwaHlsbywgc3VibGluZWFnZS4xNC5sb3dlcmNsYWRlLmxpc3QpLCJ4Il0KCnBhc3RlMCgiVE1SQ0EgZm9yIHN1YmxpbmVhZ2UgMTQgbG93ZXIgY2xhZGU6ICIsc3VibGluZWFnZS4xNC5sb3dlcmNsYWRlLmxpc3QudG1yY2EpCgojIEdldCBNUkNBIGRhdGUgZm9yIHVwcGVyIGNsYWRlCnN1YmxpbmVhZ2UuMTQudXBwZXJjbGFkZS5saXN0LnRtcmNhIDwtIHN1YmxpbmVhZ2UuMTQudHJlZS5oZWF0bWFwLmRhdGFbc3VibGluZWFnZS4xNC50cmVlLmhlYXRtYXAuZGF0YSRub2RlPT1nZXRNUkNBKEV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90KGZ1bGwuYmVhc3QyLnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBteS5zdWJsaW5lYWdlID0gMTQpQHBoeWxvLCBzdWJsaW5lYWdlLjE0LnVwcGVyY2xhZGUubGlzdCksIngiXQoKcGFzdGUwKCJUTVJDQSBmb3Igc3VibGluZWFnZSAxNCB1cHBlciBjbGFkZTogIixzdWJsaW5lYWdlLjE0LnVwcGVyY2xhZGUubGlzdC50bXJjYSkKYGBgClwKXApFeHRyYWN0IGtleSBpbmZvcm1hdGlvbiBmb3Igc3VibGluZWFnZSA2ICh0d28gc2FtcGxlcykKYGBge3J9CnN1YmxpbmVhZ2UuNi50cmVlLmhlYXRtYXAgPC0gcGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfUEhFX21ldGFkYXRhKHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX0hQRChFeHRyYWN0X3N1YmxpbmVhZ2VfdHJlZV9mb3JfcGxvdChmdWxsLmJlYXN0Mi50cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXkuc3VibGluZWFnZSA9IDYpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwiMjAxOS0wNi0wMSIpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCkKCnN1YmxpbmVhZ2UuNi50cmVlLmhlYXRtYXAuZGF0YSA8LSBzdWJsaW5lYWdlLjYudHJlZS5oZWF0bWFwJGRhdGEKCiMgR2V0IE1SQ0EgZGF0ZSBmb3IgdXBwZXIgY2xhZGUKc3VibGluZWFnZS42LmJlYXN0dHJlZS50bXJjYSA8LSBhcy5udW1lcmljKHN1YmxpbmVhZ2UuNi50cmVlLmhlYXRtYXAuZGF0YVtzdWJsaW5lYWdlLjYudHJlZS5oZWF0bWFwLmRhdGEkbm9kZT09Z2V0TVJDQShFeHRyYWN0X3N1YmxpbmVhZ2VfdHJlZV9mb3JfcGxvdChmdWxsLmJlYXN0Mi50cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXkuc3VibGluZWFnZSA9IDYpQHBoeWxvLCBjKCJQSEUxMzAwNDhBIiwgIlBIRTE2MDI4M0EiKSksImJyYW5jaCJdKQoKCnBhc3RlMCgiVE1SQ0EgZm9yIHN1YmxpbmVhZ2UgNiB1cHBlciBjbGFkZTogIixzdWJsaW5lYWdlLjYuYmVhc3R0cmVlLnRtcmNhKQpgYGAKCgoKXApcCiMjIyBFeHRyYWN0IHNhbXBsZSAmIHBvcHVsYXRpb24gc3RhdGlzdGljcyBmcm9tIGRhdGFzZXRzIGZvciB1c2UgaW4gbWFudXNjcmlwdCB0ZXh0ClwKRGF0YXNldCBhbmQgR2VvZ3JhcGhpY2FsIGRpc3RyaWJ1dGlvbnMKYGBge3J9CiMgZGF0YXNldCBjb3VudHMKcGFzdGUwKCJUb3RhbCBVSyBzYW1wbGVzIGluIGNsZWFuZWQvZGVkdXBsaWNhdGVkIGRhdGFzZXQ6ICIsbnJvdyhQSEUubWV0YWRhdGEubGlua2VkKSkKcGFzdGUwKCJPZiB3aGljaDogIixucm93KFBIRS5tZXRhZGF0YS5saW5rZWRbUEhFLm1ldGFkYXRhLmxpbmtlZCRpcy5QSEU9PSJQSEUiLF0pLCIgZnJvbSBQSEUgUmVmIGxhYiBhdCBDb2xpbmRhbGUiKQpwYXN0ZTAoIk9mIHdoaWNoOiAiLG5yb3coUEhFLm1ldGFkYXRhLmxpbmtlZFtQSEUubWV0YWRhdGEubGlua2VkJGlzLlBIRT09Ik90aGVyIixdKSwiIGZyb20gb3RoZXIgbGFicyIpCgojIHByb3BvcnRpb24gd2l0aCBnZW9ncmFwaGljYWwgZGF0YQpwYXN0ZTAoIkZyb20gVUsgc2FtcGxlcywgIiwgbnJvdyhQSEUubWV0YWRhdGEubGlua2VkWyhQSEUubWV0YWRhdGEubGlua2VkJHBoZV9jZW50cmUgJW5vdGluJSBjKCJOb3QgS25vd24iLCJVSyAobm90IEVuZ2xhbmQpIikpLF0pLCIgd2VyZSBncm91cGVkIGludG8gb25lIG9mIHRoZSA5IFBIIHJlZ2lvbnMiKQpwYXN0ZTAoIkZyb20gVUsgc2FtcGxlcywgIiwgbnJvdyhQSEUubWV0YWRhdGEubGlua2VkW1BIRS5tZXRhZGF0YS5saW5rZWQkcGhlX2NlbnRyZT09IlVLIChub3QgRW5nbGFuZCkiLF0pLCAiIHdlcmUgcmVmZXJyZWQgZnJvbSBvdXRzaWRlIEVuZ2xhbmQiKQpwYXN0ZTAoIkZyb20gVUsgc2FtcGxlcywgIiwgbnJvdyhQSEUubWV0YWRhdGEubGlua2VkW1BIRS5tZXRhZGF0YS5saW5rZWQkcGhlX2NlbnRyZT09Ik5vdCBLbm93biIsXSksICIgaGFkIHVua25vd24gcmVnaW9uIikKCiMgY291bnRzICYgZnJhY3Rpb25zIGJ5IFBIRSByZWdpb24KUEhFLmdlby5jb3VudAoKYGBgClwKR2VuZGVyIE9yaWVudGF0aW9uIHN0YXRzCmBgYHtyfQpQSEUub3JpZW50YXRpb24uY291bnRzClBIRS5nZW8ub3JpZW50YXRpb24uY291bnRzClBIRS5nZW8uSElWLmNvdW50cwpQSEUuc3VibGluZWFnZS5vcmllbnRhdGlvbi5jb3VudHMKUEhFLnN1YmxpbmVhZ2UuQWdlCmBgYAoKXApTdWJsaW5lYWdlIERpc3RyaWJ1dGlvbnMKYGBge3J9ClBIRS5MaW5lYWdlLmNvdW50ClBIRS5zdWJsaW4uY291bnQKUEhFLmdlby5zdWJsaW5lYWdlCmBgYAoKXApNYWNyb2xpZGUgcmVzaXN0YW5jZSBzdGF0cwpgYGB7cn0KVUsubWFjcm9saWRlLnJlcyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JQogIGRwbHlyOjpncm91cF9ieShBMjA1OEcsIEEyMDU5RykgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudC5hbGxlbGU9bigpKSAlPiUKICBkcGx5cjo6dW5ncm91cCgpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuY291bnQ9c3VtKENvdW50LmFsbGVsZSksIHBlcmMuYWxsZWxlPXJvdW5kKChDb3VudC5hbGxlbGUvdG90YWwuY291bnQpKjEwMCwxKSkKVUsubWFjcm9saWRlLnJlcwoKVUsubWFjcm9saWRlLnJlcy5zdWJsaW4gPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIEEyMDU4RywgQTIwNTlHKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50LmFsbGVsZT1uKCkpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLmNvdW50PXN1bShDb3VudC5hbGxlbGUpLCBwZXJjLmFsbGVsZT1yb3VuZCgoQ291bnQuYWxsZWxlL3RvdGFsLmNvdW50KSoxMDAsMSkpClVLLm1hY3JvbGlkZS5yZXMuc3VibGluCgoKIyBDYWxjdWxhdGUgbG9uZyBmb3JtIGRmLCB3aXRoIGRpZmZlcmVudCAyM1MgYWxsZWxlcyAoQTIwNThHLCBBMjA1OUcsIFdULCBVbmNlcnRhaW4pIHYucy4gc3VibGluZWFnZQpVSy5tYWNyb2xpZGUucmVzLnN1Ymxpbi5sb25nIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lCiAgbXV0YXRlKFJlc2lzdGFuY2UuYWxsZWxlPWlmZWxzZShBMjA1OEc9PSJZZXMiLCAiQTIwNThHIiwgaWZlbHNlKEEyMDU5Rz09IlllcyIsICJBMjA1OUciLCBpZmVsc2UoKEEyMDU4Rz09Ik5vIiAmIEEyMDU5Rz09Ik5vIiksIldpbGQgVHlwZSIsICJVbmNlcnRhaW4iKSkpKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIFJlc2lzdGFuY2UuYWxsZWxlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50LnBlci5zdWJsaW4uTWFjcm9saWRlcz1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuc3VibGluPXN1bShDb3VudC5wZXIuc3VibGluLk1hY3JvbGlkZXMpLCAKICAgICAgICAgICAgICAgIGZyYWN0aW9uPUNvdW50LnBlci5zdWJsaW4uTWFjcm9saWRlcy90b3RhbC5zdWJsaW4pICU+JQogICNkcGx5cjo6dW5ncm91cCgpICU+JQogIGRwbHlyOjphcnJhbmdlKChSZXNpc3RhbmNlLmFsbGVsZSksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGN1bV9mcmFjdCA9IGN1bXN1bShmcmFjdGlvbikpICU+JQogIGRwbHlyOjptdXRhdGUoY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpICU+JQogIGRwbHlyOjptdXRhdGUoUmVzaXN0YW5jZS5hbGxlbGUgPSBmYWN0b3IoUmVzaXN0YW5jZS5hbGxlbGUsIGxldmVscz1yZXYoYygiQTIwNThHIiwgIkEyMDU5RyIsICJVbmNlcnRhaW4iLCAiV2lsZCBUeXBlIikpKSkKCiMgTWFrZSBwbG90IG9mIG1hY3JvbGlkZSByZXNpc3RhbmNlIGJ5IHN1YmxpbmVhZ2VzCnAuc3VibGluLk1hY3JvbGlkZXMuaGJhcnBsb3QgPC0gZ2dwbG90KFVLLm1hY3JvbGlkZS5yZXMuc3VibGluLmxvbmcsIGFlcyhDb3VudC5wZXIuc3VibGluLk1hY3JvbGlkZXMsIHk9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGZpbGw9UmVzaXN0YW5jZS5hbGxlbGUpKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iTWFjcm9saWRlXG5SZXNpc3RhbmNlXG5BbGxlbGUiLHZhbHVlcz1jKCJpbmRpYW5yZWQyIiwgInN0ZWVsYmx1ZTEiLCJncmV5NTUiLCAiZ3JleTkwIiksIGJyZWFrcz1jKCJBMjA1OEciLCAiQTIwNTlHIiwgIlVuY2VydGFpbiIsICJXaWxkIFR5cGUiKSkgKwogIGxhYnMoeT0iVFBBIFN1YmxpbmVhZ2UiLCB4PSJQcm9wb3J0aW9uIHdpdGggTWFjcm9saWRlIFJlc2lzdGFuY2UgQWxsZWxlIikgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChuY29sPTIpKSArCiAgZ2VvbV90ZXh0KGRhdGE9VUsubWFjcm9saWRlLnJlcy5zdWJsaW4ubG9uZywgYWVzKGN1bV9mcmFjdC5taWQsIHk9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsbGFiZWw9Q291bnQucGVyLnN1Ymxpbi5NYWNyb2xpZGVzKSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICBOVUxMCgpwLnN1Ymxpbi5NYWNyb2xpZGVzLmhiYXJwbG90CgoKIyBDb21iaW5lIHBsb3Qgd2l0aCBzdWJsaW5lYWdlIGNvdW50IGJhcnMKcC5zdWJsaW4uTWFjcm9saWRlcy5oYmFycGxvdC5jb21iaSA8LSBwbG90X2dyaWQocC5zdWJsaW5lYWdlLmhiYXJwbG90ICsgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5jb2w9MykpLCBwLnN1Ymxpbi5NYWNyb2xpZGVzLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgbnJvdz0xLCBhbGlnbj1ULCBsYWJlbHM9YygiQSIsICJCIiksIGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUpCgpwLnN1Ymxpbi5NYWNyb2xpZGVzLmhiYXJwbG90LmNvbWJpCgojZ2dzYXZlKHBhc3RlMChGaWd1cmVfb3V0cHV0X2RpcmVjdG9yeSwiU3VwRmlnOV9UUEEtUEhFX1N1Ymxpbi1NYWNyb2xpZGUtUmVzLiIsZm9ybWF0KFN5cy5EYXRlKCksIiVZJW0lZCIpLCIucGRmIiksIHVuaXRzPSdtbScsIHdpZHRoPTE2MCwgaGVpZ2h0PTEyMCwgZGV2aWNlPSdwZGYnLCBkcGk9MTIwMCkKCmBgYApcClwKCiMjIyBQYWlyd2lzZSBTTlAgYW5hbHlzaXMKXApPSywgd2FudCB0byBpbnZlc3RpZ2F0ZSB0aGUgZGlmZmVyZW50IHBhdHRlcm5zIG9ic2VydmFibGUgZm9yIHRoZSBOb3J0aCBFYXN0IG9mIEVuZ2xhbmQgKHBhbGUgYmx1ZSkgaW4gU3VibGluZWFnZSAxClwKTXVsdGlwbGUgd2F5cyB3ZSBjYW4gZG8gdGhpcyAtIGluY2x1ZGluZyBTTlAgZGlzdGFuY2VzIChhbHNvIG11bHRpcGxlIHdheXMgdG8gZG8gdGhhdCkKXApgYGB7cn0KIyMjCiNVc2UgcGh5bG9nZW5ldGljIGRpc3RhbmNlIGZyb20gdGhlIFNOUCBzY2FsZWQgdHJlZQpUUEEucHlqYXIudHJlZS5zdWJzZXQudWsuY29waGVuZXRpYy5TTlAuZGlzdCA8LSBhcGU6OmNvcGhlbmV0aWMucGh5bG8oVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrKQpUUEEucHlqYXIudHJlZS5zdWJzZXQudWsuY29waGVuZXRpYy5TTlAuZGlzdC5tZWx0IDwtIGRhdGEuZnJhbWUoVGF4YTE9cm93Lm5hbWVzKFRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0KSwgVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrLmNvcGhlbmV0aWMuU05QLmRpc3QsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSAlPiUgdGlkeXI6OmdhdGhlcihUYXhhMiwgRGlzdGFuY2UuUGh5bG8sIC1UYXhhMSkKIyBUYXhhIENvbXBhcmlzb25zIGxhYmVsClRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0Lm1lbHQkVGF4YV9jb21iaW5hdGlvbiA8LSBzYXBwbHkoMTpucm93KFRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0Lm1lbHQpLCBmdW5jdGlvbiAoeCkgcGFzdGUwKHNvcnQoYyhhcy5jaGFyYWN0ZXIoVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrLmNvcGhlbmV0aWMuU05QLmRpc3QubWVsdCRUYXhhMVt4XSksYXMuY2hhcmFjdGVyKFRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0Lm1lbHQkVGF4YTJbeF0pKSksY29sbGFwc2U9Il9fXyIpKQojIE1lcmdlIHRvZ2V0aGVyCiNUUEEuV0dTLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdCA8LSBkcGx5cjo6bGVmdF9qb2luKFRQQS5XR1MuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0LCBUUEEucHlqYXIudHJlZS5zdWJzZXQudWsuY29waGVuZXRpYy5TTlAuZGlzdC5tZWx0WyxjKCJUYXhhX2NvbWJpbmF0aW9uIiwiRGlzdGFuY2UuUGh5bG8iKV0sIGJ5PSJUYXhhX2NvbWJpbmF0aW9uIikKClRQQS5XR1MuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0IDwtIFRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0Lm1lbHQKCgpUUEEuV0dTLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdCA8LSB1bmlxdWUoVFBBLldHUy5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQpCmBgYApcCk9rLCBub3cgYnJpbmcgaW4gc29tZSBtZXRhZGF0YSBhbmQgY29tcGFyaXNvbnMKYGBge3J9CiMgQnJpbmcgaW4gYW5kIG1lcmdlIG1ldGFkYXRhClBIRS5tZXRhLnBhaXJ3aXNlLnQxIDwtIFBIRS5tZXRhZGF0YS5saW5rZWRbLGMoIlNhbXBsZV9OYW1lIiwieWVhciIsInBoZV9jZW50cmUiLCJsb25kb24iLCJnZW5kZXJfb3JpZW50YXRpb24iLCJoaXZwb3MiLCJhZ2VfZ3JvdXAiLCJ1a2Jvcm4iLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIsICJUUEFfTGluZWFnZSIsIkdlb19Db3VudHJ5IiwiaXMuVUsiLCJpcy5QSEUiLCAiU2FtcGxlX1llYXIiLCJkYXRlLmRlY2ltYWwiKV0KCmNvbG5hbWVzKFBIRS5tZXRhLnBhaXJ3aXNlLnQxKSA8LSBwYXN0ZTAoY29sbmFtZXMoUEhFLm1ldGEucGFpcndpc2UudDEpLCIudDEiKQpjb2xuYW1lcyhQSEUubWV0YS5wYWlyd2lzZS50MSlbMV0gPC0gIlRheGExIgpQSEUubWV0YS5wYWlyd2lzZS50MiA8LSBQSEUubWV0YWRhdGEubGlua2VkWyxjKCJTYW1wbGVfTmFtZSIsInllYXIiLCJwaGVfY2VudHJlIiwibG9uZG9uIiwiZ2VuZGVyX29yaWVudGF0aW9uIiwiaGl2cG9zIiwiYWdlX2dyb3VwIiwidWtib3JuIiwiVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UiLCAiVFBBX0xpbmVhZ2UiLCJHZW9fQ291bnRyeSIsImlzLlVLIiwiaXMuUEhFIiwgIlNhbXBsZV9ZZWFyIiwiZGF0ZS5kZWNpbWFsIildCmNvbG5hbWVzKFBIRS5tZXRhLnBhaXJ3aXNlLnQyKSA8LSBwYXN0ZTAoY29sbmFtZXMoUEhFLm1ldGEucGFpcndpc2UudDIpLCIudDIiKQpjb2xuYW1lcyhQSEUubWV0YS5wYWlyd2lzZS50MilbMV0gPC0gIlRheGEyIgoKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIHBseXI6OmpvaW4oVFBBLldHUy5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQsUEhFLm1ldGEucGFpcndpc2UudDEsIGJ5PSJUYXhhMSIsIHR5cGU9ImxlZnQiKSAKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIHBseXI6OmpvaW4oUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLFBIRS5tZXRhLnBhaXJ3aXNlLnQyLCBieT0iVGF4YTIiLCB0eXBlPSJsZWZ0IikKCiMgRXhjbHVkZSBtaXNzaW5nIGRhdGEgKGUuZy4gbWlzc2luZyBzdWJsaW5lYWdlKSAtIHRoaXMgd2lsbCBhbHNvIHJlbW92ZSBub24tVUsgc2FtcGxlcywgc2luY2UgZnVsbCBtZXRhZGF0YSBpcyBtaXNzaW5nIGhlcmUKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YVshaXMubmEoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLnQxKSxdClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGFbIWlzLm5hKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEEucGluZWNvbmUuc3VibGluZWFnZS50MiksXQoKYGBgCgpcCkRlZmluZSBjb21wYXJpc29ucwpgYGB7cn0KIyBTYW1lIHNhbXBsZQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5zYW1wbGUgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUYXhhMT09UEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRheGEyLCJzYW1lIiwgImRpZmZlcmVudCIpCgojIFllYXJzIGJldHdlZW4gc2FtcGxlcwpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkeWVhci5kaXN0YW5jZSA8LSBhYnMoYXMubnVtZXJpYyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkeWVhci50MSkgLSBhcy5udW1lcmljKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSR5ZWFyLnQyKSkKClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRTYW1wbGVfWWVhci5kaXN0YW5jZSA8LSBhYnMoYXMubnVtZXJpYyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkU2FtcGxlX1llYXIudDEpIC0gYXMubnVtZXJpYyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkU2FtcGxlX1llYXIudDIpKQoKIyBZZWFycyBiZXR3ZWVuIGRlY2ltYWwgZGF0ZSAobW9yZSBwcmVjaXNlIHRlbXBvcmFsIGRpc3RhbmNlKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlIDwtIGFicyhhcy5udW1lcmljKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkYXRlLmRlY2ltYWwudDEpIC0gYXMubnVtZXJpYyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGF0ZS5kZWNpbWFsLnQyKSkKCiMgRXBpZGVtaW9sb2dpY2FsIHRpbWUgYmV0d2VlbiAtIGNhdGFnb3JpY2FsClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRlcGkudGltZS5kaXN0YW5jZS5jYXQgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8MS8xMiwibW9udGgiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9My8xMiwgInF1YXJ0ZXIiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9Ni8xMiwgImhhbGYgeWVhciIsIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlPD0xLCAiMSB5ZWFyIixpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9MiwgIjIgeWVhcnMiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9MywgIjMgeWVhcnMiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9NCwgIjQgeWVhcnMiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9NSwgIjUgeWVhcnMiLCAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTYsICI2IHllYXJzIiwiPjYgeWVhcnMiKSkpKSkpKSkpCgpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZXBpLnRpbWUuZGlzdGFuY2UuY2F0IDwtIGZhY3RvcihQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZXBpLnRpbWUuZGlzdGFuY2UuY2F0LCBsZXZlbHM9YygibW9udGgiLCAicXVhcnRlciIsImhhbGYgeWVhciIsIjEgeWVhciIsICIyIHllYXJzIiwgIjMgeWVhcnMiLCAiNCB5ZWFycyIsICI1IHllYXJzIiwgIjYgeWVhcnMiLCAiPjYgeWVhcnMiKSkKClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRlcGkudGltZS5kaXN0YW5jZS5jYXQueWVhcnMgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTEsICIwIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTIsICIxIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTMsICIyIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTQsICIzIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTUsICI0IiwgIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlPD02LCAiNSIsIj41IikpKSkpKQoKCiMgU2FtZSBjb3VudHJ5ClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLmNvdW50cnkgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRHZW9fQ291bnRyeS50MSA9PSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkR2VvX0NvdW50cnkudDIsICJzYW1lIiwgImRpZmZlcmVudCIpCgojIElzIFVLClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRib3RoLnVrIDwtIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkaXMuVUsudDEgPT0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGlzLlVLLnQyLCAic2FtZSIsICJkaWZmZXJlbnQiKQoKIyBJcyBQSEUKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGJvdGguUEhFIDwtIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkaXMuUEhFLnQxID09IFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRpcy5QSEUudDIsICJzYW1lIiwgImRpZmZlcmVudCIpCgojIFNhbWUgVFBBIExpbmVhZ2UgKGNsZWFuZWQgdXAgY2xhc3NpZmljYXRpb25zKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5UUEEuTGluZWFnZSA8LSBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRQQV9MaW5lYWdlLnQxPT1QSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkVFBBX0xpbmVhZ2UudDIsICJzYW1lIiwgImRpZmZlcmVudCIpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlRQQS5MaW5lYWdlIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhKSwgZnVuY3Rpb24oeCkgaWZlbHNlKChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkVFBBX0xpbmVhZ2UudDFbeF09PSIwIiB8IFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEFfTGluZWFnZS50Mlt4XT09IjAiKSxOQSxQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5UUEEuTGluZWFnZVt4XSkpCgojIFNhbWUgVFBBIHN1YmxpbmVhZ2UKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuVFBBLlBpbmVjb25lLmNsdXN0ZXIgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEEucGluZWNvbmUuc3VibGluZWFnZS50MT09UEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLnQyLCJzYW1lIiwgImRpZmZlcmVudCIpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlRQQS5QaW5lY29uZS5jbHVzdGVyIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhKSwgZnVuY3Rpb24oeCkgaWZlbHNlKCgoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuc2FtcGxlW3hdPT0iZGlmZmVyZW50IiAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEEucGluZWNvbmUuc3VibGluZWFnZS50MVt4XT09IlNpbmdsZXRvbiIpIHwoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuc2FtcGxlW3hdPT0iZGlmZmVyZW50IiAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEEucGluZWNvbmUuc3VibGluZWFnZS50Mlt4XT09IlNpbmdsZXRvbiIpKSwiZGlmZmVyZW50IixQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5UUEEuUGluZWNvbmUuY2x1c3Rlclt4XSkpCgojIERlZmluZSBHZW5ldGljIHJlbGF0aW9uc2hpcHMgaGllcmFyY2hpY2FsbHkKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbm9taWMuY2x1c3Rlci5oaWVyYXJjaHkgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSREaXN0YW5jZT09MCwiWmVyb19TTlBzIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlRQQS5QaW5lY29uZS5jbHVzdGVyPT0ic2FtZSIsIlNhbWUgU3VibGluZWFnZSIsIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5UUEEuTGluZWFnZT09InNhbWUiLCAiU2FtZSBMaW5lYWdlIiwiRGlmZmVyZW50IExpbmVhZ2UiKSkpCgpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2Vub21pYy5jbHVzdGVyLmhpZXJhcmNoeS5waCA8LSBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJERpc3RhbmNlLlBoeWxvPT0wLCJaZXJvX1NOUHMiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuVFBBLlBpbmVjb25lLmNsdXN0ZXI9PSJzYW1lIiwiU2FtZSBTdWJsaW5lYWdlIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlRQQS5MaW5lYWdlPT0ic2FtZSIsICJTYW1lIExpbmVhZ2UiLCJEaWZmZXJlbnQgTGluZWFnZSIpKSkKCgojIFNhbWUgUEhFIHJlZ2lvbgpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5QSEUucmVnaW9uIDwtIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkcGhlX2NlbnRyZS50MT09UEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHBoZV9jZW50cmUudDIsICJzYW1lIiwgImRpZmZlcmVudCIpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRQSEUuY2VudHJlLmNvbWJpbmF0aW9uIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhKSwgZnVuY3Rpb24gKHgpIHBhc3RlMChzb3J0KGMoYXMuY2hhcmFjdGVyKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRwaGVfY2VudHJlLnQxW3hdKSxhcy5jaGFyYWN0ZXIoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHBoZV9jZW50cmUudDJbeF0pKSksY29sbGFwc2U9Il9fXyIpKQoKIyBkb2VzIHRoZSBjb21iaW5hdGlvbiBpbmNsdWRlZCBMb25kb24/ClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRpbnZvbHZlcy5Mb25kb24gPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRwaGVfY2VudHJlLnQxPT0iTG9uZG9uIiB8IFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRwaGVfY2VudHJlLnQyPT0iTG9uZG9uIiwgIkxvbmRvbiIsICJub3QtTG9uZG9uIikKCgojIE9yaWVudGF0aW9uIHBhaXIKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJE9yaWVudGF0aW9uX2NvbWJpbmF0aW9uIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhKSwgZnVuY3Rpb24gKHgpIHBhc3RlMChzb3J0KGMoYXMuY2hhcmFjdGVyKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF0pLGFzLmNoYXJhY3RlcihQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2VuZGVyX29yaWVudGF0aW9uLnQyW3hdKSkpLGNvbGxhcHNlPSJfX18iKSkKCiNQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkT3JpZW50YXRpb24uQ2xhc3MgPC0gc2FwcGx5KDE6bnJvdyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEpLCBmdW5jdGlvbiAoeCkgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJNU00iICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50Mlt4XT09Ik1TTSIsICJNU00iLAojICAgICAgIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2VuZGVyX29yaWVudGF0aW9uLnQxW3hdPT0iTVNNIiB8IFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDJbeF09PSJNU00iLCAiTWl4ZWQiLCAKIyAgICAgICAgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJNU1ciICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50Mlt4XT09IldTTSIsIkhldGVyb3NleHVhbCIsIAojICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJXU00iICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50Mlt4XT09Ik1TVyIsIkhldGVyb3NleHVhbCIsIlVua25vd24iKSkpKSkKClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRPcmllbnRhdGlvbi5DbGFzcyA8LSBzYXBwbHkoMTpucm93KFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSksIGZ1bmN0aW9uICh4KSBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50MVt4XT09IkdCTVNNIiAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDJbeF09PSJHQk1TTSIsICJHQk1TTSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF0gJWluJSBjKCJNU1ciLCJXU00iKSAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDJbeF0gJWluJSBjKCJNU1ciLCJXU00iKSwiSGV0ZXJvc2V4dWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJHQk1TTSIgJiBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2VuZGVyX29yaWVudGF0aW9uLnQyW3hdICVpbiUgYygiTVNXIiwiV1NNIiksICJNaXhlZCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF0gJWluJSBjKCJNU1ciLCJXU00iKSAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJHQk1TTSIsICJNaXhlZCIsICJVbmtub3duIikpKSkpCiAgICAgICAgICAgICAgICAgICAgCgoKIyBDb3VudHJ5IENvbXBhcmlzb25zIGxhYmVsClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRDb3VudHJ5X2NvbWJpbmF0aW9ucyA8LSBwYXN0ZTAoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJEdlb19Db3VudHJ5LnQxLCJfX18iLFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRHZW9fQ291bnRyeS50MikKCiMgU3Vic2V0IHRvIFBIRSBkYXRhIG9ubHkgKGVmZmVjdGl2ZWx5IGFscmVhZHkgZG9uZSwgYnV0IGxldCdzIGJlIGV4cGxpY2l0KQpQSEUuVFBBLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YVsoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGJvdGgudWs9PSJzYW1lIiAmICBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkYm90aC5QSEU9PSJzYW1lIiksXQpQSEUuVFBBLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIFBIRS5UUEEuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGFbUEhFLlRQQS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRQSEUub25seT09IlBIRSIsXQoKUEhFLlRQQS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGFbKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRib3RoLnVrPT0ic2FtZSIpLF0KCmBgYApcClwKCmBgYHtyfQojIE1ha2Ugc2luZ2xlIHNpZGVkClBIRS5UUEEuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEgPC0gUEhFLlRQQS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YVshZHVwbGljYXRlZChQSEUuVFBBLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRheGFfY29tYmluYXRpb24pLF0KCmBgYAoKClwKXAojIyMgUGVyZm9ybSBhIG1vcmUgZGV0YWlsZWQgYW5hbHlzaXMgb2Ygc2FtcGxlcyBmcm9tIHRoZSBOb3J0aCBFYXN0IG9mIEVuZ2xhbmQKXApEbyBhIG1vcmUgZGV0YWlsZWQgZXhwbG9yYXRpb24gb2YgdGhlIE5vcnRoIEVhc3Qgb2YgRW5nbGFuZApcCmBgYHtyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD00fQpQSEUubWV0YWRhdGEubGlua2VkMi5yZWdpb25fTm9ydGhFYXN0IDwtIFBIRS5tZXRhZGF0YS5saW5rZWRbUEhFLm1ldGFkYXRhLmxpbmtlZCRwaGVfY2VudHJlPT0iTm9ydGggRWFzdCIsXQoKIyBDb25zdHJhaW4gYnkgc2FtcGxlcyBiZWluZyBmcm9tIHRoZSBOb3J0aCBFYXN0ClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhWyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkcGhlX2NlbnRyZS50MT09Ik5vcnRoIEVhc3QiICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuc2FtcGxlPT0iZGlmZmVyZW50IiksXQoKIyBDb25zdHJhaW4gYnkgdGhlIHNhbWUgUEhFIHJlZ2lvbgpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnNbUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycyRzYW1lLlBIRS5yZWdpb249PSJzYW1lIixdCgojSnVzdCBwbG90IHRoZXNlIGRpc3Ryb3MKcC5Ob3J0aEVhc3QuUGFpcndpc2UuU05Qcy51bmNvbnN0cmFpbmVkIDwtIGdncGxvdChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLCBhZXMoRGlzdGFuY2UuUGh5bG8pKSArIAogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArCiAgbGFicyh4PSJQYWlyd2lzZSBTTlAgRGlzdGFuY2UiLCB5PSJDb21wYXJpc29uIENvdW50IikKCnAuTm9ydGhFYXN0LlBhaXJ3aXNlLlNOUHMudW5jb25zdHJhaW5lZApgYGAKClwKTWFrZSBhIHNpbmdsZSBsaW5rYWdlIG5ldHdvcmsgZnJvbSB0aGUgTm9ydGggRWFzdCBzYW1wbGVzCmBgYHtyfQoKIyBDb25zdHJhaW4gYnkgU05QIGRpc3RhbmNlIChsb29zZXIgdGhhbiBwcmV2aW91c2x5IC0gd2UganVzdCB3YW50IHRvIGZpbmQgYmFzaWMgZ3JvdXBpbmdzIHdpdGhpbiBzdWJsaW5lYWdlIDEgZm9yIE5FIHNhbXBsZXMpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVyc1tQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzJERpc3RhbmNlLlBoeWxvPD0yLF0KCiMgQW5kIG1ha2Ugc3VyZSB0aGF0IHdlIGFjdHVhbGx5IGhhdmUgZ2VuZXRpYyBkaXN0YW5jZSBkYXRhIGZvciBhbGwgc2FtcGxlcyB3aXRoaW4gdGhlIG5ldHdvcmsKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycyA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzWyFpcy5uYShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzJERpc3RhbmNlLlBoeWxvKSxdCgojIGNsZWFudXAgc29tZSBkYXRhIG5vaXNlClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVyc1shaXMubmEoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycyR5ZWFyLnQxKSxdCgojIHByZXBhcmUgaW50cHV0IGRhdGEgKHdpdGggZWRnZSBpbmZvKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzWyxjKCJUYXhhMSIsIlRheGEyIiwiRGlzdGFuY2UuUGh5bG8iLCJkZWNpbWFsLmRhdGUuZGlzdGFuY2UiLCJ5ZWFyLmRpc3RhbmNlIiwiT3JpZW50YXRpb24uQ2xhc3MiLCJlcGkudGltZS5kaXN0YW5jZS5jYXQiKV0KCiMjIyMjIyMjIyMjIwojIHNvbWUgaXNzdWVzIHdpdGggdXBkYXRlIHRvIFI0IC0gZG91YmxlIHNpZGVkIG1hdHJpeApQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSRlZGdlbmFtZSA8LSBzYXBwbHkoMTpucm93KFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxKSwgZnVuY3Rpb24oeCkgcGFzdGUwKHNvcnQoYXMuY2hhcmFjdGVyKHVubGlzdChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MVt4LGMoIlRheGExIiwiVGF4YTIiKV0pKSksY29sbGFwc2U9Il9fXyIpKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MVshZHVwbGljYXRlZChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSRlZGdlbmFtZSksXQoKIyBBbHNvIGhhdmluZyBhbiBpc3N1ZSB3aXRoIHRheGEgYXMgZmFjdG9ycyBoZXJlClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxJFRheGExIDwtIGFzLmNoYXJhY3RlcihQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSRUYXhhMSkKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycy5pbnB1dDEkVGF4YTIgPC0gYXMuY2hhcmFjdGVyKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxJFRheGEyKQojIyMjIyMjIyMjIyMKCiNpbnZlcnNlIHdlaWdodApQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSREaXN0YW5jZS5pbnYgPC0gMS9QSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSREaXN0YW5jZS5QaHlsbwoKIyBNYWtlIGFjdHVhbCBuZXR3b3JrCnNldC5zZWVkKDEyMzUpClBIRS5Ob3J0aEVhc3QubmV0d29yayA8LSBuZXR3b3JrKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxLCBtYXRyaXgudHlwZSA9ICJlZGdlbGlzdCIsIGlnbm9yZS5ldmFsID0gRkFMU0UsIGRpcmVjdGVkID0gRikKClBIRS5Ob3J0aEVhc3QubmV0d29yay5nZyA8LSBnZ25ldHdvcmsoUEhFLk5vcnRoRWFzdC5uZXR3b3JrLCBsYXlvdXQgPSAia2FtYWRha2F3YWkiLCB3ZWlnaHRzID0gIkRpc3RhbmNlLmludiIpClBIRS5Ob3J0aEVhc3QubmV0d29yay5nZyRUYXhhMSA8LSBQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuZ2ckdmVydGV4Lm5hbWVzCgojIGV4dHJhY3QgdGVtcG9yYWwgY2x1c3RlcnMgZnJvbSBuZXR3b3JrClBIRS5Ob3J0aEVhc3QubmV0d29yay5pZyA8LSBhc0lncmFwaChQSEUuTm9ydGhFYXN0Lm5ldHdvcmspClBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzIDwtIGRhdGEuZnJhbWUoVGF4YTE9bmV0d29yay52ZXJ0ZXgubmFtZXMoUEhFLk5vcnRoRWFzdC5uZXR3b3JrKSwgdmVydGV4Lm5vPWFzLnZlY3RvcihWKFBIRS5Ob3J0aEVhc3QubmV0d29yay5pZykpLCBjbHVzdGVyPWlncmFwaDo6Y29tcG9uZW50cyhQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuaWcpJG1lbWJlcnNoaXApCiMgRm9yIGVhc2Ugb2Ygc3RvcnkgdGVsbGluZyBpbiB0aGUgcGFwZXIsIGZsaXAgY2x1c3RlcnMgMiBhbmQgMyBhcm91bmQgKHNvIHdlIGNhbiB0YWxrIGFib3V0IDIgZmlyc3QpClBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzIDwtIFBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzICU+JQogIGRwbHlyOjptdXRhdGUoY2x1c3Rlci5vbGQ9Y2x1c3RlciwgY2x1c3Rlcj1pZmVsc2UoY2x1c3Rlci5vbGQ9PTIsIDMsIGlmZWxzZShjbHVzdGVyLm9sZD09MywyLGNsdXN0ZXIub2xkKSkpClBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJENsdXN0ZXIgPC0gcGFzdGUwKCJDbHVzdGVyIixQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRjbHVzdGVyKQoKIyBtZXJnZSBtZXRhZGF0YSBiYWNrIGluClBIRS5Ob3J0aEVhc3QubmV0d29yay5nZyA8LSBwbHlyOjpqb2luKFBIRS5Ob3J0aEVhc3QubmV0d29yay5nZywgZGF0YS5mcmFtZShUYXhhMT1QSEUubWV0YWRhdGEubGlua2VkJFNhbXBsZV9OYW1lLCBQSEUubWV0YWRhdGEubGlua2VkWyxjKCJwaGVfY2VudHJlIiwibG9uZG9uIiwieWVhciIsImFnZV9ncm91cCIsInVrYm9ybiIsImdlbmRlcl9vcmllbnRhdGlvbiIsImhpdnBvcyIsIlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIiwiVFBBX0xpbmVhZ2UiKV0sIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSxieT0iVGF4YTEiLCB0eXBlPSJsZWZ0IikKClBIRS5Ob3J0aEVhc3QubmV0d29yay5nZyA8LSBwbHlyOjpqb2luKFBIRS5Ob3J0aEVhc3QubmV0d29yay5nZywgZGF0YS5mcmFtZShUYXhhMT1QSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRUYXhhMSwgQ2x1c3Rlcj1QSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRDbHVzdGVyKSwgYnk9IlRheGExIiwgdHlwZT0ibGVmdCIpCgpgYGAKXApQbG90IG5ldHdvcmsKYGBge3J9CiMgUGxvdCBuZXR3b3JrCnAuUEhFLk5vcnRoRWFzdC5uZXR3b3JrLjJTTlAgPC0gZ2dwbG90KFBIRS5Ob3J0aEVhc3QubmV0d29yay5nZywgYWVzKHggPSB4LCB5ID0geSwgeGVuZCA9IHhlbmQsIHllbmQgPSB5ZW5kKSkgKyAKICBnZW9tX2VkZ2VzKGFscGhhPTAuOTAsIGN1cnZhdHVyZSA9IDAuMiwgYWVzKGNvbG9yPWZhY3RvcihEaXN0YW5jZS5QaHlsbyksIGxpbmV0eXBlPWZhY3RvcihEaXN0YW5jZS5QaHlsbykpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJncmV5NSIsImdyZXk1NSIsImdyZXk4NSIpLCBuYW1lPSJTTlBcbkRpc3RhbmNlIikgKwogIHNjYWxlX2xpbmV0eXBlKG5hbWU9IlNOUFxuRGlzdGFuY2UiKSArCiAgdGhlbWVfYmxhbmsoKSArCiAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2NvbG9yKCkgKyBnZ25ld3NjYWxlOjpuZXdfc2NhbGUoInNpemUiKSArCiAgZ2VvbV9ub2RlbGFiZWwoYWVzKGNvbG9yPWdlbmRlcl9vcmllbnRhdGlvbiwgbGFiZWw9cGFzdGUoVGF4YTEseWVhcixzZXA9IlxuIiksZm9udGZhY2UgPSAiYm9sZCIpLCBhbHBoYT0wLjgsIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbi0wLjQsIGxhYmVsLnNpemU9MC4xNSwgbGFiZWwucGFkZGluZyA9IHVuaXQoMC4wNSwgImxpbmVzIikpICsKICBnZW9tX25vZGVzKHNpemU9MS4wLCBhZXMoY29sb3I9Z2VuZGVyX29yaWVudGF0aW9uKSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iR2VuZGVyXG5PcmllbnRhdGlvbiIsIHZhbHVlcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbi5jb2xzLCBicmVha3M9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24pICsgCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICBOVUxMCnAuUEhFLk5vcnRoRWFzdC5uZXR3b3JrLjJTTlAKCmBgYAoKClwKT2ssIHNvIHRocmVlIG5ldHdvcmtzLiBDbGVhciBkaWZmZXJlbnRpYXRpb24gb2YgYSBoZXRlcm9zZXh1YWwgbmV0d29yayAod2l0aCAwLXNucCBkaXN0YW5jZXMpIGFuZCB0d28gcHJlZG9taW5hbnRseSBNU00gbmV0d29ya3MKXApMZXQncyBsb29rIGF0IHRoZSBwaHlsb2dlbmV0aWMgY29udGV4dCBvZiB0aG9zZSBOb3J0aCBFYXN0IGNsdXN0ZXJzIHdlJ3ZlIGRlZmluZWQuClB1bGwgb3V0IHN1YnRyZWVzIChmcm9tIHN1YmxpbmVhZ2UgMSBzdWJ0cmVlKQpcCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTJ9CiMgQ2x1c3RlciAxCkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEgPC0gZ2V0TVJDQShmdWxsLmJlYXN0Mi50cmVlQHBoeWxvLCBQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50c1tQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRDbHVzdGVyPT0iQ2x1c3RlcjEiLCJUYXhhMSJdKQpCZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUgPC0gdHJlZV9zdWJzZXQoZnVsbC5iZWFzdDIudHJlZSwgbm9kZT1CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLCBsZXZlbHNfYmFjaz0wKQoKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUgPC0gcGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfUEhFX21ldGFkYXRhKHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX0hQRChCZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCIyMDE5LTA2LTAxIiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBpbml0aWFsLnRyYWNrLm9mZnNldCA9IDEwKQoKIyBDYW4ndCBmaXQgaW4gdGlwIGxhYnMsIGJ1dCBzaW5jZSB0aGlzIGlzIGEgcG9seXBoeWxldGljIHN1YnRyZWUsIGl0IHdvdWxkIGJlIGhlbHBmdWwgdG8gYWRkIGEgdHJhY2sgdG8gaGlnaGxpZ2h0IHRoZSBORSBzdHJhaW5zClBIRS5tZXRhZGF0YS5saW5rZWQkaXMuTm9ydGhFYXN0IDwtIGlmZWxzZShQSEUubWV0YWRhdGEubGlua2VkJHBoZV9jZW50cmU9PSJOb3J0aCBFYXN0IiwiTm9ydGggRWFzdCIsICJPdGhlciBFbmdsYW5kIikKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgPC0gZ2hlYXRtYXAocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVBIRS5tZXRhZGF0YS5saW5rZWQkU2FtcGxlX05hbWUsIGBOb3J0aCBFYXN0YD1QSEUubWV0YWRhdGEubGlua2VkJGlzLk5vcnRoRWFzdCksIGNvbG9yPU5VTEwsd2lkdGg9KDEvbWF4KHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlJGRhdGEkaGVpZ2h0KSozKSwgb2Zmc2V0PTEwKyg0KjUpLGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9Ik5vcnRoIEVhc3RcbkVuZ2xhbmQiLCB2YWx1ZXM9YygiI0E2Q0VFMyIsImdyZXk5NSIpLCBicmVha3M9YygiTm9ydGggRWFzdCIsIk90aGVyIEVuZ2xhbmQiKSwgbmEudmFsdWUgPSAid2hpdGUiLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDUpKSArCiAgICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCgojIEp1c3QgY29uZmlybSB0aGUgQ2x1c3RlcklEcyBmb3IgdGhpcyBzdWJ0cmVlIChtYWtlIHN1cmUgaXQgZG9lc24ndCBlbmNsb3NlIG90aGVyIGNsdXN0ZXJzKQpwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC53aXRoX2NsdXN0ZXJJRCA8LSBnaGVhdG1hcChwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCwgZGF0YS5mcmFtZShyb3cubmFtZXM9UEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkVGF4YTEsIENsdXN0ZXJJRD1QSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRDbHVzdGVyKSwgY29sb3I9TlVMTCx3aWR0aD0oMS9tYXgocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUkZGF0YSRoZWlnaHQpKjMpLCBvZmZzZXQ9MTArKDQqNiksY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsIGZvbnQuc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluKSArIAogICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iTm9ydGggRWFzdFxuQ2x1c3RlciIsIHZhbHVlcz1jKCIjN2ZjOTdmIiwiI2JlYWVkNCIsIiNmZGMwODYiKSwgYnJlYWtzPWMoIkNsdXN0ZXIxIiwiQ2x1c3RlcjIiLCJDbHVzdGVyMyIpLCBuYS52YWx1ZSA9ICJ3aGl0ZSIsIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gNikpICsKICAgIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKCiMgYWRkIGEgYml0IG1vcmUgcm9vbSB0byB0aGUgeCBheGlzCnAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LnguYXhpcy5saW1pdHMgPC0gZ2dwbG90X2J1aWxkKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LndpdGhfY2x1c3RlcklEKSRsYXlvdXQkcGFuZWxfc2NhbGVzX3hbWzFdXSRyYW5nZSRyYW5nZQpwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC53aXRoX2NsdXN0ZXJJRCA8LSBwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC53aXRoX2NsdXN0ZXJJRCArIAogICAgY29vcmRfY2FydGVzaWFuKHg9YyhwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC54LmF4aXMubGltaXRzWzFdLHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LnguYXhpcy5saW1pdHNbMl0rNCksIHk9YygtMC41LShsZW5ndGgodW5pcXVlKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0JGRhdGEkbGFiZWwpKS8xNSksbGVuZ3RoKHVuaXF1ZShwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCRkYXRhJGxhYmVsKSkrMikpICsKICB0aGVtZShsZWdlbmQubWFyZ2luID0gbWFyZ2luKC0wLjUsMCwwLDAsIHVuaXQ9Im1tIikpCiNwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC53aXRoX2NsdXN0ZXJJRAoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDbHVzdGVyIDIKQmVhc3QudHJlZS5ORS5jbHVzdGVyMiA8LSBnZXRNUkNBKGZ1bGwuYmVhc3QyLnRyZWVAcGh5bG8sIFBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzW1BIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJENsdXN0ZXI9PSJDbHVzdGVyMiIsIlRheGExIl0pCkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZSA8LSB0cmVlX3N1YnNldChmdWxsLmJlYXN0Mi50cmVlLCBub2RlPUJlYXN0LnRyZWUuTkUuY2x1c3RlcjIsIGxldmVsc19iYWNrPTEpCgpwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZSA8LSBwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9QSEVfbWV0YWRhdGEocGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfSFBEKEJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIjIwMTktMDYtMDEiKSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIGluaXRpYWwudHJhY2sub2Zmc2V0ID0gMjApICsgZ2VvbV90aXBsYWIoc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBhbGlnbj1ULCBvZmZzZXQ9NSwgbGluZXNpemU9MC40KQojIEp1c3QgYWRkIENsdXN0ZXJJRHMgZm9yIHRoaXMgc3VidHJlZSB0byBoaWdobGlnaHQKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUgPC0gZ2hlYXRtYXAocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJFRheGExLCBDbHVzdGVySUQ9UEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3RlciksIGNvbG9yPU5VTEwsd2lkdGg9KDEvbWF4KHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlJGRhdGEkaGVpZ2h0KSozKSwgb2Zmc2V0PTIwKyg0KjUpLGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9Ik5vcnRoIEVhc3RcbkNsdXN0ZXIiLCB2YWx1ZXM9YygiIzdmYzk3ZiIsIiNiZWFlZDQiLCIjZmRjMDg2IiksIGJyZWFrcz1jKCJDbHVzdGVyMSIsIkNsdXN0ZXIyIiwiQ2x1c3RlcjMiKSwgbmEudmFsdWUgPSAid2hpdGUiLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDUsIG5jb2w9MikpICsKICAgIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKIyBhZGQgYSBiaXQgbW9yZSByb29tIHRvIHRoZSB4IGF4aXMKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUueC5heGlzLmxpbWl0cyA8LSBnZ3Bsb3RfYnVpbGQocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUpJGxheW91dCRwYW5lbF9zY2FsZXNfeFtbMV1dJHJhbmdlJHJhbmdlCnAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlIDwtIHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlICsgCiAgICBjb29yZF9jYXJ0ZXNpYW4oeD1jKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlLnguYXhpcy5saW1pdHNbMV0scC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUueC5heGlzLmxpbWl0c1syXSsxMiksIHk9YygtMC41LShsZW5ndGgodW5pcXVlKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlJGRhdGEkbGFiZWwpKS8yMCktMSxsZW5ndGgodW5pcXVlKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlJGRhdGEkbGFiZWwpKSswLjUpKSArIAogIHRoZW1lKGxlZ2VuZC5tYXJnaW4gPSBtYXJnaW4oLTAuNSwwLDAsMCwgdW5pdD0ibW0iKSkKCiNwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENsdXN0ZXIgMwpCZWFzdC50cmVlLk5FLmNsdXN0ZXIzIDwtIGdldE1SQ0EoZnVsbC5iZWFzdDIudHJlZUBwaHlsbywgUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHNbUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3Rlcj09IkNsdXN0ZXIzIiwiVGF4YTEiXSkKQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlIDwtIHRyZWVfc3Vic2V0KGZ1bGwuYmVhc3QyLnRyZWUsIG5vZGU9QmVhc3QudHJlZS5ORS5jbHVzdGVyMywgbGV2ZWxzX2JhY2s9MSkKCnAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlIDwtIHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX1BIRV9tZXRhZGF0YShwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9IUEQoQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwiMjAxOS0wNi0wMSIpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgaW5pdGlhbC50cmFjay5vZmZzZXQgPSAyNikgKyBnZW9tX3RpcGxhYihzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGFsaWduPVQsIG9mZnNldD0zLCBsaW5lc2l6ZT0wLjQpCgojIEp1c3QgYWRkIENsdXN0ZXJJRHMgZm9yIHRoaXMgc3VidHJlZSB0byBoaWdobGlnaHQKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUgPC0gZ2hlYXRtYXAocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJFRheGExLCBDbHVzdGVySUQ9UEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3RlciksIGNvbG9yPU5VTEwsd2lkdGg9KDEvbWF4KHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlJGRhdGEkaGVpZ2h0KSozKSwgb2Zmc2V0PTI2Kyg0KjUpLGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9Ik5vcnRoIEVhc3RcbkNsdXN0ZXIiLCB2YWx1ZXM9YygiIzdmYzk3ZiIsIiNiZWFlZDQiLCIjZmRjMDg2IiksIGJyZWFrcz1jKCJDbHVzdGVyMSIsIkNsdXN0ZXIyIiwiQ2x1c3RlcjMiKSwgbmEudmFsdWUgPSAid2hpdGUiLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDUsIG5jb2w9MikpICsKICAgIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKCiMgYWRkIGEgYml0IG1vcmUgcm9vbSB0byB0aGUgeCBheGlzCnAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlLnguYXhpcy5saW1pdHMgPC0gZ2dwbG90X2J1aWxkKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlKSRsYXlvdXQkcGFuZWxfc2NhbGVzX3hbWzFdXSRyYW5nZSRyYW5nZQpwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZSA8LSBwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZSArIAogICAgY29vcmRfY2FydGVzaWFuKHg9YyhwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZS54LmF4aXMubGltaXRzWzFdLHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlLnguYXhpcy5saW1pdHNbMl0rMTIpLCB5PWMoLTAuNS0obGVuZ3RoKHVuaXF1ZShwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZSRkYXRhJGxhYmVsKSkvMjApLTEsbGVuZ3RoKHVuaXF1ZShwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZSRkYXRhJGxhYmVsKSkrMC41KSkgKyAKICB0aGVtZShsZWdlbmQubWFyZ2luID0gbWFyZ2luKC0wLjUsMCwwLDAsIHVuaXQ9Im1tIikpCiNwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZQoKI3AuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LndpdGhfY2x1c3RlcklECiNwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZSAKI3AuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlIApgYGAKClwKU2luY2UgQ2x1c3RlciAxIGlzIHJlYWxseSBxdWl0ZSBwb2x5cGh5bGV0aWMsIGl0IG1heWJlIG1vcmUgdXNlZnVsIHRvIHNob3cgdGhlIGNsdXN0ZXJzIGluIGNvbnRleHQgZm9yIHRoYXQgb25lCgpgYGB7cn0KIyBBZGQgTm9ydGggRWFzdCBpZGVudGlmaWVyIGNvbHVtbgpwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCA8LSBnaGVhdG1hcChzdWJsaW5lYWdlLjEudHJlZS5oZWF0bWFwLCBkYXRhLmZyYW1lKHJvdy5uYW1lcz1QSEUubWV0YWRhdGEubGlua2VkJFNhbXBsZV9OYW1lLCBgTm9ydGggRWFzdGA9UEhFLm1ldGFkYXRhLmxpbmtlZCRpcy5Ob3J0aEVhc3QpLCBjb2xvcj1OVUxMLHdpZHRoPSgxL21heChzdWJsaW5lYWdlLjEudHJlZS5oZWF0bWFwJGRhdGEkaGVpZ2h0KSozKSoxLjIsIG9mZnNldD0wKyg0KjUpKjEuMixjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCwgZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJOb3J0aCBFYXN0XG5FbmdsYW5kIiwgdmFsdWVzPWMoIiNBNkNFRTMiLCJncmV5OTUiKSwgYnJlYWtzPWMoIk5vcnRoIEVhc3QiLCJPdGhlciBFbmdsYW5kIiksIG5hLnZhbHVlID0gIndoaXRlIiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSA1KSkgKwogICAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2ZpbGwoKQoKIyBKdXN0IGNvbmZpcm0gdGhlIENsdXN0ZXJJRHMgZm9yIHRoaXMgc3VidHJlZSAobWFrZSBzdXJlIGl0IGRvZXNuJ3QgZW5jbG9zZSBvdGhlciBjbHVzdGVycykKcC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgPC0gZ2hlYXRtYXAocC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJFRheGExLCBDbHVzdGVySUQ9UEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3RlciksIGNvbG9yPU5VTEwsd2lkdGg9KDEvbWF4KHAuQmVhc3QudHJlZS5zdWJsaW5lYWdlMS5ORS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0JGRhdGEkaGVpZ2h0KSozKSoxLjIsIG9mZnNldD0wKyg0KjYpKjEuMixjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCwgZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJOb3J0aCBFYXN0XG5DbHVzdGVyIiwgdmFsdWVzPWMoIiM3ZmM5N2YiLCIjYmVhZWQ0IiwiI2ZkYzA4NiIpLCBicmVha3M9YygiQ2x1c3RlcjEiLCJDbHVzdGVyMiIsIkNsdXN0ZXIzIiksIG5hLnZhbHVlID0gIndoaXRlIiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSA2LCBuY29sPTIpKSArCiAgICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCgojIGFkZCBhIGJpdCBtb3JlIHJvb20gdG8gdGhlIHggYXhpcwpwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC54LmF4aXMubGltaXRzIDwtIGdncGxvdF9idWlsZChwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCkkbGF5b3V0JHBhbmVsX3NjYWxlc194W1sxXV0kcmFuZ2UkcmFuZ2UKcC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgPC0gcC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgKyAKICAgIGNvb3JkX2NhcnRlc2lhbih4PWMocC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQueC5heGlzLmxpbWl0c1sxXSxwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC54LmF4aXMubGltaXRzWzJdKzQpLCB5PWMoLTAuNS0obGVuZ3RoKHVuaXF1ZShwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCRkYXRhJGxhYmVsKSkvMTUpLGxlbmd0aCh1bmlxdWUocC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQkZGF0YSRsYWJlbCkpKzIpKQoKIyByZWR1Y2Ugc3BhY2luZyBiZXR3ZWVuIGxlZ2VuZCBzY2FsZXMKcC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgPC0gcC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgKyB0aGVtZShsZWdlbmQubWFyZ2luID0gbWFyZ2luKC0wLjk1LDAsMCwwLCB1bml0PSJtbSIpKQpwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodAoKYGBgCgoKXCAKUGxvdCB0b2dldGhlcgpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQoKcC5CZWFzdC50cmVlLk5FLnN1YnRyZWVzLmNvbWJpMSA8LSBwbG90X2dyaWQocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUsIHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlLCBuY29sPTEsIGxhYmVscz1jKCJDIC0gQ2x1c3RlciAyIiwgIkQgLSBDbHVzdGVyIDMiKSwgdmp1c3Q9MS4wLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplLCBzY2FsZT0wLjk1KQoKcC5CZWFzdC50cmVlLk5FLnN1YnRyZWVzLmNvbWJpMiA8LSBwbG90X2dyaWQocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQud2l0aF9jbHVzdGVySUQsIHAuQmVhc3QudHJlZS5ORS5zdWJ0cmVlcy5jb21iaTEsIG5jb2w9MiwgcmVsX3dpZHRocz1jKDMsMiksIGxhYmVscz1jKCJCIC0gQ2x1c3RlciAxIiwgIiIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplKQpwLkJlYXN0LnRyZWUuTkUuc3VidHJlZXMuY29tYmkyCgoKcC5CZWFzdC50cmVlLk5FLnN1YnRyZWVzLmNvbWJpMyA8LSBwbG90X2dyaWQocC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQsIHAuQmVhc3QudHJlZS5ORS5zdWJ0cmVlcy5jb21iaTEsIG5jb2w9MiwgcmVsX3dpZHRocz1jKDgsNyksIGxhYmVscz1jKCJCIC0gU3VibGluZWFnZSAxIChBbGwpIiwgIiIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplLCBzY2FsZT0wLjk1LCB2anVzdD0xLjApCgpwLkJlYXN0LnRyZWUuTkUuc3VidHJlZXMuY29tYmkzCgpgYGAKClwKXApMb29rIG1vcmUgY2xvc2VseSBhdCBwb3B1bGF0aW9uIGRlbW9ncmFwaGljcyBvZiB0aGVzZSBjbHVzdGVycwpgYGB7cn0KIyBNZXRhZGF0YSBvbiBORSBjbHVzdGVyIDIKUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6OmZpbHRlcihTYW1wbGVfTmFtZSAlaW4lIEJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZUBwaHlsbyR0aXAubGFiZWwpICU+JQogIGRwbHlyOjpncm91cF9ieShHZW9fQ291bnRyeSwgaXMuTm9ydGhFYXN0LCBnZW5kZXJfb3JpZW50YXRpb24pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKQoKIyBNZXRhZGF0YSBvbiBORSBjbHVzdGVyIDMKUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6OmZpbHRlcihTYW1wbGVfTmFtZSAlaW4lIEJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZUBwaHlsbyR0aXAubGFiZWwpICU+JQogIGRwbHlyOjpncm91cF9ieShHZW9fQ291bnRyeSwgaXMuTm9ydGhFYXN0LCBnZW5kZXJfb3JpZW50YXRpb24pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKQoKIyBDb3VudHJ5IGluZm8gb24gTkUgY2x1c3RlciAzClRQQS5tZXRhMi4xICU+JSAKICBkcGx5cjo6ZmlsdGVyKFNhbXBsZV9OYW1lICVpbiUgQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlQHBoeWxvJHRpcC5sYWJlbCkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KEdlb19Db3VudHJ5KSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkKCiMgU2VwYXJhdGUgbWV0YWRhdGEgcmVjb3JkcyBzaG93IEh1bmdhcmlhbiBzYW1wbGUgIlRQQV9IVU4xODAwMDEiIGNhbWUgZnJvbSBhIG1hbGUgYmlzZXh1YWwgKE1TV00pLgpgYGAKXApFeGFtaW5lIFNOUCBzY2FsZWQgdHJlZSBmb3IgZGlzdGFuY2VzCmBgYHtyfQoKIyBFeHRyYWN0IGluZm9ybWF0aW9uIGFib3V0IFNOUCBkaXN0YW5jZXMKVFBBLk5FY2x1c3RlcjMucHlqYXJ0cmVlLm1yY2EgPC0gZ2V0TVJDQShUUEEucHlqYXIudHJlZSwgYXMuY2hhcmFjdGVyKHVubGlzdChUUEEubWV0YTIuMVtUUEEubWV0YTIuMSRTYW1wbGVfTmFtZSAlaW4lIEJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZUBwaHlsbyR0aXAubGFiZWwsIlNhbXBsZV9OYW1lIl0pKSkKCgpUUEEuTkVjbHVzdGVyMy5weWphcnRyZWUuc3VidHJlZSA8LSB0cmVlX3N1YnNldChUUEEucHlqYXIudHJlZSwgbm9kZT1UUEEuTkVjbHVzdGVyMy5weWphcnRyZWUubXJjYSwgbGV2ZWxzX2JhY2s9MSkKCmdndHJlZShUUEEuTkVjbHVzdGVyMy5weWphcnRyZWUuc3VidHJlZSkgKyBnZW9tX3RpcGxhYihzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pCmdndHJlZShUUEEuTkVjbHVzdGVyMy5weWphcnRyZWUuc3VidHJlZSkkZGF0YQpgYGAKClwKXApEbyBzb21lIGFuYWx5c2lzIG9mIG5lYXJlc3QgbmVpZ2hib3VyIGFuZCBkaXN0YW5jZXMgdG8gTVJDQXMKYGBge3J9CmNhbGN1bGF0ZS55ZWFycy5mcm9tLm1yY2EgPC0gZnVuY3Rpb24oY3VycmVudC5nZ3RyZWUucGh5bG8sIGN1cnJlbnQuZ2d0cmVlLmRhdGEpewogICNjdXJyZW50LmdndHJlZSA8LSBCZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUKICBhbGwudGlwcyA8LSBjdXJyZW50LmdndHJlZS5waHlsbyR0aXAubGFiZWwKICBkaXN0LjIubXJjYSA8LSBOVUxMCiAgIyMjIHB1dCBkYXRlcyBpbnRvIGRmCiAgY3VycmVudC5nZ3RyZWUuZGF0YSRtcmNhLm1lZGlhbiA8LSAyMDE5LjUgLSBjdXJyZW50LmdndHJlZS5kYXRhJGhlaWdodF9tZWRpYW4KICBjdXJyZW50LmdndHJlZS5kYXRhJHllYXIgPC0gYXMubnVtZXJpYyhyb3VuZCgyMDE5LjUgLSBjdXJyZW50LmdndHJlZS5kYXRhJGhlaWdodF9tZWRpYW4sMykpCiAgY3VycmVudC5nZ3RyZWUuZGF0YSRtcmNhLjk1aGlnaCA8LSByb3VuZCgyMDE5LjUgLSBzYXBwbHkoMTpucm93KGN1cnJlbnQuZ2d0cmVlLmRhdGEpLGZ1bmN0aW9uKHgpIGFzLm51bWVyaWModW5saXN0KGN1cnJlbnQuZ2d0cmVlLmRhdGFbeCwiaGVpZ2h0XzAuOTVfSFBEIl0pKVsxXSksIDMpCiAgY3VycmVudC5nZ3RyZWUuZGF0YSRtcmNhLjk1bG93IDwtIHJvdW5kKDIwMTkuNSAtIHNhcHBseSgxOm5yb3coY3VycmVudC5nZ3RyZWUuZGF0YSksZnVuY3Rpb24oeCkgYXMubnVtZXJpYyh1bmxpc3QoY3VycmVudC5nZ3RyZWUuZGF0YVt4LCJoZWlnaHRfMC45NV9IUEQiXSkpWzJdKSwgMykKICAjIGV4dHJhY3QgZGF0ZXMgYmV0d2VlbiBzYW1wbGUgYW5kIGl0cyBNUkNBIHVzaW5nIGxvb3AKICBmb3IgKGN1cnJlbnQubm9kZSBpbiBhbGwudGlwcykgewogICAgY3VycmVudC5wYXJlbnQgPC0gYyhtYXRjaChjdXJyZW50Lm5vZGUsY3VycmVudC5nZ3RyZWUucGh5bG8kdGlwLmxhYmVsKSwgcGhhbmdvcm46OkFuY2VzdG9ycyhjdXJyZW50LmdndHJlZS5waHlsbywgbWF0Y2goYyhjdXJyZW50Lm5vZGUpLCBjdXJyZW50LmdndHJlZS5waHlsbyR0aXAubGFiZWwpLCAicGFyZW50IikpCiAgICAKICAgIGN1cnJlbnQubm9kZWxpc3QgPC0gY3VycmVudC5nZ3RyZWUuZGF0YVtjdXJyZW50LmdndHJlZS5kYXRhJG5vZGUgJWluJSBjdXJyZW50LnBhcmVudCxdCiAgICBjdXJyZW50LmRpc3QuMi5tcmNhIDwtIGMoY3VycmVudC5ub2RlLCBhcy5udW1lcmljKGN1cnJlbnQubm9kZWxpc3RbMSwieWVhciJdLWN1cnJlbnQubm9kZWxpc3RbMiwieWVhciJdKSkKICAgIGRpc3QuMi5tcmNhIDwtIHJiaW5kKGRpc3QuMi5tcmNhLCBjdXJyZW50LmRpc3QuMi5tcmNhKQogIH0KICBkaXN0LjIubXJjYSA8LSBkYXRhLmZyYW1lKFNhbXBsZV9OYW1lPWFzLmNoYXJhY3RlcihkaXN0LjIubXJjYVssMV0pLCBkaXN0LnRvLm1yY2E9YXMubnVtZXJpYyhkaXN0LjIubXJjYVssMl0pLCBzdHJpbmdzQXNGYWN0b3JzPUYpCiAgcmV0dXJuKGRpc3QuMi5tcmNhKQp9CgojIyMgQWxsIHNhbXBsZXMgaW4gZ2xvYmFsIHRyZWUKZGlzdC5tcmNhLmFsbC5UUEEgPC0gY2FsY3VsYXRlLnllYXJzLmZyb20ubXJjYShmdWxsLmJlYXN0Mi50cmVlQHBoeWxvLCBmdWxsLmJlYXN0Mi50cmVlQGRhdGEpCgpgYGAKXApNZXJnZSBkaXN0Mk1SQ0Egd2l0aCBtZXRhZGF0YQpgYGB7cn0KUEhFLm1ldGFkYXRhLmxpbmtlZC5kaXN0Mm1yY2EgPC0gbGVmdF9qb2luKFBIRS5tZXRhZGF0YS5saW5rZWQsIGRpc3QubXJjYS5hbGwuVFBBLCBieT0iU2FtcGxlX05hbWUiKQoKcC50aW1lMm1yY2Eub3JpZW50YXRpb24gPC0gZ2dwbG90KFBIRS5tZXRhZGF0YS5saW5rZWQuZGlzdDJtcmNhLCBhZXMoZ2VuZGVyX29yaWVudGF0aW9uLCBkaXN0LnRvLm1yY2EsIGNvbG9yPWdlbmRlcl9vcmllbnRhdGlvbikpICsgCiAgZ2VvbV9xdWFzaXJhbmRvbShzaXplPTAuNzUsIGFscGhhPTAuNSkgKwogIHRoZW1lX2xpZ2h0KCkgKyB0aGVtZS50ZXh0LnNpemUgKwogIGNvb3JkX2ZsaXAoKSArCiAgbGFicyh4PSJHZW5kZXIgT3JpZW50YXRpb24iLCB5PSJZZWFycyB0byBNUkNBIiwgY29sb3I9IkdlbmRlciBPcmllbnRhdGlvbiIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249J2JvdHRvbScsIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IkdlbmRlclxuT3JpZW50YXRpb24iLCB2YWx1ZXM9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24uY29scywgYnJlYWtzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uKQoKcC50aW1lMm1yY2EucGhlX3JlZ2lvbiA8LSBnZ3Bsb3QoUEhFLm1ldGFkYXRhLmxpbmtlZC5kaXN0Mm1yY2EsIGFlcyhwaGVfY2VudHJlLCBkaXN0LnRvLm1yY2EsIGNvbG9yPXBoZV9jZW50cmUpKSArIAogIGdlb21fcXVhc2lyYW5kb20oc2l6ZT0wLjc1LCBhbHBoYT0wLjUpICsKICB0aGVtZV9saWdodCgpICsgdGhlbWUudGV4dC5zaXplICsKICBjb29yZF9mbGlwKHlsaW09YygwLDQwKSkgKwogIGxhYnMoeD0iVUtIU0EgUmVnaW9uIiwgeT0iWWVhcnMgdG8gTVJDQSIsIGNvbG9yPSJVS0hTQSBSZWdpb24iKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLCBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSJVS0hTQVxuUmVnaW9uIiwgdmFsdWVzPVBIRS5yZWdpb24uY29scy5icmV3JHJlZ2lvbi5jb2wsIGJyZWFrcz1QSEUucmVnaW9uLmNvbHMuYnJldyRVS0hTQS5yZWdpb24pCgpwLnRpbWUybXJjYS5waGVfcmVnaW9uLm9yaWVudGF0aW9uIDwtIGdncGxvdChQSEUubWV0YWRhdGEubGlua2VkLmRpc3QybXJjYSwgYWVzKHBoZV9jZW50cmUsIGRpc3QudG8ubXJjYSwgY29sb3I9Z2VuZGVyX29yaWVudGF0aW9uKSkgKyAKICBnZW9tX3F1YXNpcmFuZG9tKHNpemU9MC43NSwgYWxwaGE9MC41KSArCiAgdGhlbWVfbGlnaHQoKSArIHRoZW1lLnRleHQuc2l6ZSArCiAgY29vcmRfZmxpcCh5bGltPWMoMCwyMCkpICsKICBsYWJzKHg9IlVLSFNBIFJlZ2lvbiIsIHk9IlllYXJzIHRvIE1SQ0EiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLCBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSJHZW5kZXJcbk9yaWVudGF0aW9uIiwgdmFsdWVzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uLmNvbHMsIGJyZWFrcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbikKcC50aW1lMm1yY2EucGhlX3JlZ2lvbi5vcmllbnRhdGlvbgoKCnAudGltZTJtcmNhLnN1YmxpbmVhZ2UgPC0gZ2dwbG90KFBIRS5tZXRhZGF0YS5saW5rZWQuZGlzdDJtcmNhLCBhZXMoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGRpc3QudG8ubXJjYSwgY29sb3I9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpKSArIAogIGdlb21fcXVhc2lyYW5kb20oc2l6ZT0wLjc1LCBhbHBoYT0wLjUpICsKICB0aGVtZV9saWdodCgpICsgdGhlbWUudGV4dC5zaXplICsKICBjb29yZF9mbGlwKCkgKwogIGxhYnMoeD0iVFBBIExpbmVhZ2UiLCB5PSJZZWFycyB0byBNUkNBIiwgY29sb3I9IlRQQSBMaW5lYWdlIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJywgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMsIGJyZWFrcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSkKcC50aW1lMm1yY2Euc3VibGluZWFnZQoKCnAudGltZTJtcmNhLkxpbmVhZ2UgPC0gZ2dwbG90KFBIRS5tZXRhZGF0YS5saW5rZWQuZGlzdDJtcmNhLCBhZXMoVFBBX0xpbmVhZ2UsIGRpc3QudG8ubXJjYSwgY29sb3I9VFBBX0xpbmVhZ2UpKSArIAogIGdlb21fcXVhc2lyYW5kb20oc2l6ZT0wLjc1LCBhbHBoYT0wLjUpICsKICB0aGVtZV9saWdodCgpICsgdGhlbWUudGV4dC5zaXplICsKICBjb29yZF9mbGlwKCkgKwogIGxhYnMoeD0iVFBBIExpbmVhZ2UiLCB5PSJZZWFycyB0byBNUkNBIChNZWRpYW4gb2YgUG9zdGVyaW9yKSIsIGNvbG9yPSJUUEEgTGluZWFnZSIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249J2JvdHRvbScsIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1UUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UuY29sLCBicmVha3M9VFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlKQpgYGAKXApcCk1heWJlIGNhbiBtYWtlIGFuIE1TVCBvZiB0aGUgTm9ydGggRWFzdCBzYW1wbGVzIGZvciBncmFwZXRyZWU/CmBgYHtyfQpUUEEucHlqYXIudHJlZS5zdWJzZXQuTm9ydGhFYXN0IDwtIGFwZTo6a2VlcC50aXAoVFBBLnB5amFyLnRyZWUsIGFzLmNoYXJhY3Rlcih1bmxpc3QoUEhFLm1ldGFkYXRhLmxpbmtlZFtQSEUubWV0YWRhdGEubGlua2VkJHBoZV9jZW50cmU9PSJOb3J0aCBFYXN0IiwiU2FtcGxlX05hbWUiXSkpKQoKI2dndHJlZShUUEEucHlqYXIudHJlZS5zdWJzZXQuTm9ydGhFYXN0KQojd3JpdGUudHJlZShUUEEucHlqYXIudHJlZS5zdWJzZXQuTm9ydGhFYXN0LCBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS5VSy1vbmx5LU5vcnRoRWFzdC5weWphci4yMDIyLTAyLTI2LnRyZSIpKQoKIyBXcml0ZSBvdXQgYSBtZXRhZGF0YSBzaGVldCBmb3IgdGhlIHJlbGV2YW50IGluZm9ybWF0aW9uClBIRS5tZXRhZGF0YS5saW5rZWQuZ3JhcGV0cmVlIDwtIFBIRS5tZXRhZGF0YS5saW5rZWRbLGMoIlNhbXBsZV9OYW1lIiwgInllYXIiLCJnZW5kZXJfb3JpZW50YXRpb24iLCJwaGVfY2VudHJlIiwiaGl2cG9zIiwidWtib3JuIiwiVFBBX0xpbmVhZ2UiLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIpXQpjb2xuYW1lcyhQSEUubWV0YWRhdGEubGlua2VkLmdyYXBldHJlZSlbMV0gPC0gIklEIgoKI3dyaXRlLnRhYmxlKFBIRS5tZXRhZGF0YS5saW5rZWQuZ3JhcGV0cmVlLCBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS5VSy1vbmx5LmdyYXBldHJlZS5tZXRhLjIwMjItMDItMDMudHN2IiksIHNlcCA9ICJcdCIsIHF1b3RlPUYsIHJvdy5uYW1lcyA9IEYpCmBgYAoKCkFsdGVybmF0aXZlIGFwcHJvYWNoIHVzaW5nIE1TVCBpbnN0ZWFkIG9mIG5ldHdvcmtzIGZvciBOb3J0aCBFYXN0IGRhdGEKYGBge3J9CiMgUmVhZCBpbiBNU1QKI1RQQS5Ob3J0aEVhc3RFbmdsYW5kLkdyYXBldHJlZS5maWxlIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiVFBBLVVLLU5vcnRoRWFzdC0yMDIyLTAyLTI2LkdlbmRlck9yaWVudGF0aW9uLU1TVHJlZS5pbmtzY2FwZWQuK25vZGUtY291bnRzK0dCTVNNLnN2ZyIpCgpwLlRQQS5Ob3J0aEVhc3RFbmdsYW5kLkdyYXBldHJlZSA8LSBnZ2RyYXcoKSArIGRyYXdfaW1hZ2UoVFBBLk5vcnRoRWFzdEVuZ2xhbmQuR3JhcGV0cmVlLmZpbGUpCnAuVFBBLk5vcnRoRWFzdEVuZ2xhbmQuR3JhcGV0cmVlCgpwLlRQQS5Ob3J0aEVhc3RFbmdsYW5kLkdyYXBldHJlZS5oZWFkZXIgPC0gcGxvdF9ncmlkKHAuVFBBLk5vcnRoRWFzdEVuZ2xhbmQuR3JhcGV0cmVlLCBsYWJlbHM9YygiQSAtIE5ldHdvcmsgQ2x1c3RlcnMgKE5vcnRoIEVhc3QgRW5nbGFuZCkiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgc2NhbGU9MC45NSkKCmBgYApcClBsb3Qgd2l0aCBiZWFzdCB0cmVlcwpgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTEyfQojcC5QSEUuTm9ydGhFYXN0X01TVC53aXRoLmJlYXN0LnN1YnRyZWVzLmNvbWJpIDwtIHBsb3RfZ3JpZChwLlRQQS5Ob3J0aEVhc3RFbmdsYW5kLkdyYXBldHJlZSwgcC5CZWFzdC50cmVlLk5FLnN1YnRyZWVzLmNvbWJpMywgbmNvbD0xLCByZWxfaGVpZ2h0cz1jKDMsNiksIGxhYmVscz1jKCJBIC0gTmV0d29yayBDbHVzdGVycyAoTm9ydGggRWFzdCBFbmdsYW5kKSIsICIiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgc2NhbGUgPSAwLjk1KQoKcC5QSEUuTm9ydGhFYXN0X01TVC53aXRoLmJlYXN0LnN1YnRyZWVzLmNvbWJpIDwtIHBsb3RfZ3JpZChwLlRQQS5Ob3J0aEVhc3RFbmdsYW5kLkdyYXBldHJlZS5oZWFkZXIsIHAuQmVhc3QudHJlZS5ORS5zdWJ0cmVlcy5jb21iaTMsIG5jb2w9MSwgcmVsX2hlaWdodHM9YygzLDcpKQoKCgpwLlBIRS5Ob3J0aEVhc3RfTVNULndpdGguYmVhc3Quc3VidHJlZXMuY29tYmkKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksIkZpZzNfU3VibGluMS5Ob3J0aEVhc3QuTVNUK0JlYXN0LiIsZm9ybWF0KFN5cy5EYXRlKCksIiVZJW0lZCIpLCIucGRmIiksIHVuaXRzPSdtbScsIHdpZHRoPTIwMCwgaGVpZ2h0PTI0NSwgZGV2aWNlPSdwZGYnLCBkcGk9MTIwMCkKCgpgYGAKClwKRG8gc29tZSBhbmFseXNpcyBvZiBtYWpvciBzdWJsaW5lYWdlcyBvdmVyIHRpbWUgYnkgcmVnaW9uIC0gY291bGQgdGhpcyBpbmZsdWVuY2Ugb2JzZXJ2YXRpb25zIGFib3V0IHN1YmxpbmVhZ2VzPwpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9NH0KIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGJ5IFBIRSBSZWdpb24KUEhFLm1ham9yLnN1YmxpbmVhZ2UuUEhFY2VudHJlLmRhdGUgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6OmZpbHRlcihUUEEucGluZWNvbmUuc3VibGluZWFnZSAlaW4lIGMoMSwxNCkpICU+JQogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgcGhlX2NlbnRyZSwgeWVhcikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuc3VibGluPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MocGhlX2NlbnRyZSksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnN1YmxpbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKQoKCmdncGxvdChQSEUubWFqb3Iuc3VibGluZWFnZS5QSEVjZW50cmUuZGF0ZSwgYWVzKHllYXIsIHBoZV9jZW50cmUsIHNpemU9Q291bnQsIGNvbG9yPVRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSkgKwogIGdlb21fcG9pbnQoKSArIAogIGZhY2V0X2dyaWQoLn5UUEEucGluZWNvbmUuc3VibGluZWFnZSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZS5jb2xzLCBicmVha3M9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UpCgoKcC5QSEUubWFqb3Iuc3VibGluZWFnZS5QSEVjZW50cmUuZGF0ZS5idWJibGVwbG90IDwtIGdncGxvdChQSEUubWFqb3Iuc3VibGluZWFnZS5QSEVjZW50cmUuZGF0ZSwgYWVzKHllYXIsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBjb2xvcj1UUEEucGluZWNvbmUuc3VibGluZWFnZSkpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNjUsIGFlcyhzaXplPUNvdW50KSkgKyAKICBnZW9tX2xpbmUoYWxwaGE9MC4yNSkgKwogIGZhY2V0X2dyaWQoZmFjdG9yKGdzdWIoIlxcICIsIlxuIixwaGVfY2VudHJlKSwgbGV2ZWxzPWdzdWIoIlxcICIsIlxuIixQSEUucmVnaW9uLmNvbHMuYnJldyRVS0hTQS5yZWdpb24pKX4uLCBzd2l0Y2g9J3knKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoc3RyaXAucGxhY2VtZW50ID0gIm91dHNpZGUiKSArCiAgdGhlbWUoc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvcj0nd2hpdGUnLCBmaWxsPSd3aGl0ZScsbGluZXR5cGU9InNvbGlkIiksIHN0cmlwLnRleHQueT1lbGVtZW50X3RleHQoY29sb3IgPSAiZ3JleTI1IixhbmdsZT0wLCBzaXplPTUpKSArIAogIHNjYWxlX3NpemVfYXJlYShtYXhfc2l6ZSA9IDQuNSxicmVha3M9YygxLDUsMTAsMjAsMzAsNDApKSArCiAgdGhlbWUudGV4dC5zaXplICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMsIGJyZWFrcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSkgKyAKICBsYWJzKHk9IlJlZ2lvbiIsIHg9IlllYXIiLCBjb2xvcj0iU3VibGluZWFnZSIpIAogCnAuUEhFLm1ham9yLnN1YmxpbmVhZ2UuUEhFY2VudHJlLmRhdGUuYnViYmxlcGxvdAoKYGBgClwKRG8gc29tZSBzcGVjaWZpYyBhbmFseXNpcyBmb3IgdGhlIDMgTm9ydGhlcm4gcmVnaW9ucwpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9M30KIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGJ5IFBIRSBSZWdpb24KIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpmaWx0ZXIocGhlX2NlbnRyZSAlaW4lIGMoIk5vcnRoIEVhc3QiLCAiTm9ydGggV2VzdCIsICJZb3Jrc2hpcmUgYW5kIEh1bWJlciIpKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50PW4oKSkKCiBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6ZmlsdGVyKHBoZV9jZW50cmUgJWluJSBjKCJOb3J0aCBFYXN0IiwgIk5vcnRoIFdlc3QiLCAiWW9ya3NoaXJlIGFuZCBIdW1iZXIiKSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KHllYXIpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQ9bigpKQoKCnAuUEhFLm1ham9yLnN1YmxpbmVhZ2UuM05vcnRoZXJuUmVnaW9ucyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6ZmlsdGVyKHBoZV9jZW50cmUgJWluJSBjKCJOb3J0aCBFYXN0IiwgIk5vcnRoIFdlc3QiLCAiWW9ya3NoaXJlIGFuZCBIdW1iZXIiKSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCB5ZWFyLCBwaGVfY2VudHJlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZ2dwbG90KGFlcyh5ZWFyLCBDb3VudCwgZmlsbD1waGVfY2VudHJlKSkgKyAKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIHdpZHRoPTAuNjUpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9UEhFLnJlZ2lvbi5jb2xzLmJyZXckcmVnaW9uLmNvbCwgYnJlYWtzPVBIRS5yZWdpb24uY29scy5icmV3JFVLSFNBLnJlZ2lvbikgKwogIHRoZW1lX2J3KCkgKyB0aGVtZS50ZXh0LnNpemUgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDIwMTIsMjAxOCwxKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9cHJldHR5KSArCiAgbGFicyh0aXRsZT0iU2FtcGxlcyBpbiAzIE5vcnRoZXJuIFJlZ2lvbnMiLCB4PSJDb2xsZWN0aW9uIFllYXIiLCB5PSJTYW1wbGUgQ291bnQiLCBmaWxsPSJQdWJsaWMgSGVhbHRoXG5SZWdpb24iKSArCiAgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICAjZ2VvbV90ZXh0KGFlcyh4PXllYXIseT1Db3VudC0wLjUsIGxhYmVsPUNvdW50KSwgY29sb3I9J2dyZXk5NScsIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKwogIE5VTEwKcC5QSEUubWFqb3Iuc3VibGluZWFnZS4zTm9ydGhlcm5SZWdpb25zCgpgYGAKCgoKXApTaW5nbGUgbGlua2FnZSBuZXR3b3JrIG9mIGlkZW50aWNhbCBnZW5vbWVzIGZyb20gVUsKCmBgYHtyfQojIENvbnN0cmFpbiBieSBTTlAgZGlzdGFuY2UgKGlkZW50aWNhbCBpbiB0aGUgYXNyIHNucCB0cmVlKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2FscyA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGFbUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJERpc3RhbmNlLlBoeWxvPT0wLF0KCiMgYW5kIGEgbWF4IG9mIDIgeWVhcnMKI1BIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzW1BIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9MixdCgoKIyBBbmQgbWFrZSBzdXJlIHRoYXQgd2UgYWN0dWFsbHkgaGF2ZSBnZW5ldGljIGRpc3RhbmNlIGRhdGEgZm9yIGFsbCBzYW1wbGVzIHdpdGhpbiB0aGUgbmV0d29yawpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2FscyA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fsc1shaXMubmEoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMkRGlzdGFuY2UuUGh5bG8pLF0KCiMgcmVtb3ZlIHNlbGYtc2FtcGxlcwpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2FscyA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fsc1tQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2FscyRzYW1lLnNhbXBsZT09ImRpZmZlcmVudCIsXQoKCiMgY2xlYW51cCBzb21lIGRhdGEgbm9pc2UKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHNbIWlzLm5hKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzJHllYXIudDEpLF0KCiMgcHJlcGFyZSBpbnRwdXQgZGF0YSAod2l0aCBlZGdlIGluZm8pClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fsc1ssYygiVGF4YTEiLCJUYXhhMiIsIkRpc3RhbmNlLlBoeWxvIiwiZGVjaW1hbC5kYXRlLmRpc3RhbmNlIiwieWVhci5kaXN0YW5jZSIsIk9yaWVudGF0aW9uLkNsYXNzIiwiZXBpLnRpbWUuZGlzdGFuY2UuY2F0LnllYXJzIiwiZXBpLnRpbWUuZGlzdGFuY2UuY2F0IildCgojIyMjIyMjIyMjIyMKIyBzb21lIGlzc3VlcyB3aXRoIHVwZGF0ZSB0byBSNCAtIGRvdWJsZSBzaWRlZCBtYXRyaXgKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxJGVkZ2VuYW1lIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxKSwgZnVuY3Rpb24oeCkgcGFzdGUwKHNvcnQoYXMuY2hhcmFjdGVyKHVubGlzdChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDFbeCxjKCJUYXhhMSIsIlRheGEyIildKSkpLGNvbGxhcHNlPSJfX18iKSkKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MVshZHVwbGljYXRlZChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDEkZWRnZW5hbWUpLF0KCiMgQWxzbyBoYXZpbmcgYW4gaXNzdWUgd2l0aCB0YXhhIGFzIGZhY3RvcnMgaGVyZQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDEkVGF4YTEgPC0gYXMuY2hhcmFjdGVyKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MSRUYXhhMSkKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxJFRheGEyIDwtIGFzLmNoYXJhY3RlcihQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDEkVGF4YTIpCiMjIyMjIyMjIyMjIwojIERlZHVwbGljYXRlCgojaW52ZXJzZSB3ZWlnaHQKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZS5pbnYgPC0gMS8xLyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlKzAuMDQpCgojIE1ha2UgYWN0dWFsIG5ldHdvcmsKc2V0LnNlZWQoMTIzNikKUEhFLmlkZW50aWNhbHMubmV0d29yayA8LSBuZXR3b3JrKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MSwgbWF0cml4LnR5cGUgPSAiZWRnZWxpc3QiLCBpZ25vcmUuZXZhbCA9IEZBTFNFLCBkaXJlY3RlZCA9IEYsIGxvb3BzID0gRikKCiNQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnIDwtIGdnbmV0d29yayhQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLCBsYXlvdXQgPSAia2FtYWRha2F3YWkiLCB3ZWlnaHRzID0gImRlY2ltYWwuZGF0ZS5kaXN0YW5jZS5pbnYiKQojUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyA8LSBnZ25ldHdvcmsoUEhFLmlkZW50aWNhbHMubmV0d29yaywgbGF5b3V0ID0gImZydWNodGVybWFucmVpbmdvbGQiLCB3ZWlnaHRzID0gImRlY2ltYWwuZGF0ZS5kaXN0YW5jZSIpClBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cgPC0gZ2duZXR3b3JrKFBIRS5pZGVudGljYWxzLm5ldHdvcmssIGxheW91dCA9ICJmcnVjaHRlcm1hbnJlaW5nb2xkIikKClBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2ckVGF4YTEgPC0gUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyR2ZXJ0ZXgubmFtZXMKCiMgZXh0cmFjdCB0ZW1wb3JhbCBjbHVzdGVycyBmcm9tIG5ldHdvcmsKUEhFLmlkZW50aWNhbHMubmV0d29yay5pZyA8LSBhc0lncmFwaChQSEUuaWRlbnRpY2Fscy5uZXR3b3JrKQpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHMgPC0gZGF0YS5mcmFtZShUYXhhMT1uZXR3b3JrLnZlcnRleC5uYW1lcyhQSEUuaWRlbnRpY2Fscy5uZXR3b3JrKSwgdmVydGV4Lm5vPWFzLnZlY3RvcihWKFBIRS5pZGVudGljYWxzLm5ldHdvcmsuaWcpKSwgY2x1c3Rlcj1pZ3JhcGg6OmNvbXBvbmVudHMoUEhFLmlkZW50aWNhbHMubmV0d29yay5pZykkbWVtYmVyc2hpcCkKUEhFLmlkZW50aWNhbHMubmV0d29yay5jb21wb25lbnRzJENsdXN0ZXIgPC0gcGFzdGUwKCJDbHVzdGVyIixQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHMkY2x1c3RlcikKCiMgbWVyZ2UgbWV0YWRhdGEgYmFjayBpbgpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnIDwtIHBseXI6OmpvaW4oUEhFLmlkZW50aWNhbHMubmV0d29yay5nZywgZGF0YS5mcmFtZShUYXhhMT1QSEUubWV0YWRhdGEubGlua2VkJFNhbXBsZV9OYW1lLCBQSEUubWV0YWRhdGEubGlua2VkWyxjKCJwaGVfY2VudHJlIiwibG9uZG9uIiwieWVhciIsImFnZV9ncm91cCIsInVrYm9ybiIsImdlbmRlcl9vcmllbnRhdGlvbiIsImhpdnBvcyIsIlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIiwiVFBBX0xpbmVhZ2UiKV0sIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSxieT0iVGF4YTEiLCB0eXBlPSJsZWZ0IikKClBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cgPC0gcGx5cjo6am9pbihQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLCBkYXRhLmZyYW1lKFRheGExPVBIRS5pZGVudGljYWxzLm5ldHdvcmsuY29tcG9uZW50cyRUYXhhMSwgQ2x1c3Rlcj1QSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3RlciksIGJ5PSJUYXhhMSIsIHR5cGU9ImxlZnQiKQoKCiMgCiMgQWRkIHRlbXBvcmFsIGNvbG91ciBzY2FsZQojdW5pcXVlKFBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2ckZXBpLnRpbWUuZGlzdGFuY2UuY2F0KQoKZXBpLnRpbWUuZGlzdGFuY2UuY2F0LmNvbHMgPC0gcmV2KGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbCg4LCAiR3JleXMiKSkobGVuZ3RoKHVuaXF1ZShQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnJGVwaS50aW1lLmRpc3RhbmNlLmNhdCkpLTEpKQoKCiMgUGxvdCBuZXR3b3JrCnAuUEhFLmlkZW50aWNhbHMubmV0d29yay4wU05QIDwtIGdncGxvdChQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLCBhZXMoeCA9IHgsIHkgPSB5LCB4ZW5kID0geGVuZCwgeWVuZCA9IHllbmQpKSArIAogIGdlb21fZWRnZXMoYWxwaGE9MC45MCwgY3VydmF0dXJlID0gMC4yLCBhZXMoY29sb3I9ZmFjdG9yKGVwaS50aW1lLmRpc3RhbmNlLmNhdCksIGxpbmV0eXBlPWZhY3RvcihlcGkudGltZS5kaXN0YW5jZS5jYXQpKSkgKwogICNzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImdyZXk1IiwiZ3JleTM1IiwiZ3JleTU1IiwgImdyZXk2NSIsICJncmV5NzUiKSwgbmFtZT0iU05QXG5EaXN0YW5jZSIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iVGVtcG9yYWxcbkRpc3RhbmNlIiwgdmFsdWVzID0gZXBpLnRpbWUuZGlzdGFuY2UuY2F0LmNvbHMpICsKICBzY2FsZV9saW5ldHlwZShuYW1lPSJUZW1wb3JhbFxuRGlzdGFuY2UiKSArCiAgdGhlbWVfYmxhbmsoKSArCiAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2NvbG9yKCkgKyBnZ25ld3NjYWxlOjpuZXdfc2NhbGUoInNpemUiKSArCiAgI2dlb21fbm9kZWxhYmVsKGFlcyhjb2xvcj1nZW5kZXJfb3JpZW50YXRpb24sIGxhYmVsPXBhc3RlKFRheGExLHllYXIsc2VwPSJcbiIpLGZvbnRmYWNlID0gImJvbGQiKSwgYWxwaGE9MC44LCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4tMC40LCBsYWJlbC5zaXplPTAuMTUsIGxhYmVsLnBhZGRpbmcgPSB1bml0KDAuMDUsICJsaW5lcyIpKSArCiAgZ2VvbV9ub2RlcyhzaXplPTIuNSwgYWVzKGNvbG9yPWdlbmRlcl9vcmllbnRhdGlvbiksIGFscGhhPTAuOSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iR2VuZGVyXG5PcmllbnRhdGlvbiIsIHZhbHVlcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbi5jb2xzLCBicmVha3M9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24pICsgCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICBOVUxMCnAuUEhFLmlkZW50aWNhbHMubmV0d29yay4wU05QCgpgYGAKClBsb3QgdGhpcyBhZ2FpbnN0IGEgVUsgdHJlZT8KYGBge3J9CmdoZWF0bWFwKGdndHJlZShUUEEucHlqYXIudHJlZS5zdWJzZXQudWspLApkYXRhLmZyYW1lKHJvdy5uYW1lcz1QSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHMkVGF4YTEsIENsdXN0ZXI9UEhFLmlkZW50aWNhbHMubmV0d29yay5jb21wb25lbnRzJENsdXN0ZXIpKQoKYGBgCgoKClwKU29tZSBzdGF0cyBmcm9tIHRoaXMKYGBge3J9CnAuUEhFLmlkZW50aWNhbC5PcmllbnRhdGlvbl9jbGFzcy5ieWRhdGVkaXN0IDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSAlPiUKICBkcGx5cjo6ZmlsdGVyKHNhbWUuc2FtcGxlPT0iZGlmZmVyZW50IiwgRGlzdGFuY2UuUGh5bG89PTApICU+JQogICNmaWx0ZXIoZGVjaW1hbC5kYXRlLmRpc3RhbmNlPD0xKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoZXBpLnRpbWUuZGlzdGFuY2UuY2F0LCBPcmllbnRhdGlvbi5DbGFzcykgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQuY2xhc3MuZGF0ZT1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUoc3VtLmNsYXNzPXN1bShDb3VudC5jbGFzcy5kYXRlKSwgZnJhY3QuY2xhc3M9Q291bnQuY2xhc3MuZGF0ZS9zdW0uY2xhc3MpICU+JQogIGdncGxvdChhZXMoeD1lcGkudGltZS5kaXN0YW5jZS5jYXQsIHk9Q291bnQuY2xhc3MuZGF0ZSwgZmlsbD1PcmllbnRhdGlvbi5DbGFzcykpICsKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIHBvc2l0aW9uPSdzdGFjaycpICsKICB0aGVtZV9idygpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogIGxhYnMoeD0iVGltZSBiZXR3ZWVuIHNhbXBsZXMiLCB5PSJJbnRlcmFjdGlvbiBDb3VudCIsIGZpbGw9Ik9yaWVudGF0aW9uIFR5cGUiKQpwLlBIRS5pZGVudGljYWwuT3JpZW50YXRpb25fY2xhc3MuYnlkYXRlZGlzdAoKCiAgCnAuUEhFLmlkZW50aWNhbC5PcmllbnRhdGlvbl9jbGFzcy5ieVplcm9kaXN0LmNsdXN0ZXIgPC0gUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyAlPiUKICBkcGx5cjo6ZmlsdGVyKCFpcy5uYShPcmllbnRhdGlvbi5DbGFzcykpICU+JQogIGRwbHlyOjpncm91cF9ieShDbHVzdGVyLCBPcmllbnRhdGlvbi5DbGFzcykgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQuY2xhc3MuY2x1c3Rlcj1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUoc3VtLmNsYXNzPXN1bShDb3VudC5jbGFzcy5jbHVzdGVyKSwgZnJhY3QuY2xhc3M9Q291bnQuY2xhc3MuY2x1c3Rlci9zdW0uY2xhc3MpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2Moc3VtLmNsYXNzKSkgJT4lCiAgZHBseXI6OnVuZ3JvdXAoKSAlPiUKICBkcGx5cjo6bXV0YXRlKENsdXN0ZXI9YXNfZmFjdG9yKENsdXN0ZXIpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9Q2x1c3RlciwgeT1Db3VudC5jbGFzcy5jbHVzdGVyLCBmaWxsPU9yaWVudGF0aW9uLkNsYXNzKSkgKwogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JywgcG9zaXRpb249J3N0YWNrJykgKyAKICB0aGVtZV9idygpICsKICB4LnRoZW1lLmF4aXMucm90YXRlICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogIGxhYnMoeD0iSWRlbnRpY2FsIEdlbm9tZSBDbHVzdGVyIiwgeT0iSW50ZXJhY3Rpb24gQ291bnQiLCBmaWxsPSJPcmllbnRhdGlvbiBUeXBlIikKcC5QSEUuaWRlbnRpY2FsLk9yaWVudGF0aW9uX2NsYXNzLmJ5WmVyb2Rpc3QuY2x1c3RlcgoKZC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciA8LSBsZWZ0X2pvaW4oUEhFLmlkZW50aWNhbHMubmV0d29yay5jb21wb25lbnRzWyxjKCJUYXhhMSIsIkNsdXN0ZXIiKV0sIFBIRS5tZXRhZGF0YS5saW5rZWRbLGMoIlNhbXBsZV9OYW1lIiwicGhlX2NlbnRyZSIsImxvbmRvbiIsInllYXIiLCJhZ2VfZ3JvdXAiLCJ1a2Jvcm4iLCJnZW5kZXJfb3JpZW50YXRpb24iLCJoaXZwb3MiLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIsIlRQQV9MaW5lYWdlIildLCBieT1jKCJUYXhhMSI9IlNhbXBsZV9OYW1lIikpICU+JQogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgQ2x1c3RlciwgZ2VuZGVyX29yaWVudGF0aW9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50Lm9yaWVudC5jbHVzdGVyPW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZShjb3VudC5jbHVzdGVyPXN1bShjb3VudC5vcmllbnQuY2x1c3RlciksIGZyYWN0PWNvdW50Lm9yaWVudC5jbHVzdGVyL2NvdW50LmNsdXN0ZXIpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhjb3VudC5jbHVzdGVyKSkgJT4lCiAgZHBseXI6Om11dGF0ZShDbHVzdGVyLm89YXNfZmFjdG9yKENsdXN0ZXIpKQoKCiMgUGxvdCBzYW1wbGUgY291bnRzIGJ5IGdlbm9tZSBjbHVzdGVyIChjb2xvdXJlZCBieSBvcmllbnRhdGlvbikKcC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciA8LSBkLlBIRS5pZGVudGljYWwuR2VuZGVyT3JpZW50YXRpb24uYnlaZXJvZGlzdC5jbHVzdGVyICU+JQogIGdncGxvdChhZXMoQ2x1c3Rlci5vLCBjb3VudC5vcmllbnQuY2x1c3RlciwgZmlsbD1nZW5kZXJfb3JpZW50YXRpb24pKSArIAogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iiwgd2lkdGg9MC42NSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkdlbmRlclxuT3JpZW50YXRpb24iLCB2YWx1ZXM9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24uY29scywgYnJlYWtzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEpKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgeC50aGVtZS5heGlzLnJvdGF0ZSArIAogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsNDUsNSkpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogIGxhYnMoeD0iSWRlbnRpY2FsIEdlbm9tZSBDbHVzdGVyIiwgeT0iU2FtcGxlIENvdW50IiwgZmlsbD0iUGF0aWVudCBHZW5kZXIgT3JpZW50YXRpb24iKSAKCiMgQWRkIGRldGFpbHMgb2Ygc3VibGluZWFnZSAgCnAuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgPC0gcC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciArIAogIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9jb2xvcigpICsKICBnZW9tX3BvaW50KGRhdGE9KGQuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgJT4lIHNlbGVjdChDbHVzdGVyLm8sIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSAlPiUgZGlzdGluY3QoKSksIGFlcyhDbHVzdGVyLm8sIC0xLjUsIGNvbG9yPVRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSwgaW5oZXJpdC5hZXMgPSBGKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UuY29scywgYnJlYWtzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLCBuYW1lPSJTdWJsaW5lYWdlIiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyKSkgKwogIE5VTEwKCiMgQWRkIGEgc3VibGluZWFnZSBheGlzIGxhYmVsIChiaXQgb2YgYSBoYWNrKQpwLlBIRS5pZGVudGljYWwuR2VuZGVyT3JpZW50YXRpb24uYnlaZXJvZGlzdC5jbHVzdGVyIDwtIHAuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgKyAKICBnZW9tX3RleHQoZGF0YT1kYXRhLmZyYW1lKGxhYj0iU3VibGluZWFnZSIsIHk9LTEuNSwgeD0yOCwgc3RyaW5nc0FzRmFjdG9ycz1GKSwgYWVzKGxhYmVsPWxhYiwgeD14LCB5PXkpLCBoanVzdCA9IDAuMSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICBjb29yZF9jYXJ0ZXNpYW4oeD1jKDEsIDI3KSwgY2xpcD0nb2ZmJykKICAKcC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlcgoKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksIlN1cEZpZzZfSWRlbnRpY2FsLVNOUC1jbHVzdF9vcmllbnRhdGlvbi4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xMjAsIGhlaWdodD0xMDAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCgpgYGAKXApQb3NzaWJsZSB0byBpbnRyb2R1Y2Ugc29tZSBtb3JlIGluZm8gaW50byB0aGF0IHBsb3Q/CgoKYGBge3J9CmQuUEhFLmlkZW50aWNhbC5yZWdpb24uYnlaZXJvZGlzdC5jbHVzdGVyIDwtIGxlZnRfam9pbihQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHNbLGMoIlRheGExIiwiQ2x1c3RlciIpXSwgUEhFLm1ldGFkYXRhLmxpbmtlZFssYygiU2FtcGxlX05hbWUiLCJwaGVfY2VudHJlIiwibG9uZG9uIiwieWVhciIsImFnZV9ncm91cCIsInVrYm9ybiIsImdlbmRlcl9vcmllbnRhdGlvbiIsImhpdnBvcyIsIlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIiwiVFBBX0xpbmVhZ2UiKV0sIGJ5PWMoIlRheGExIj0iU2FtcGxlX05hbWUiKSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBDbHVzdGVyLCBwaGVfY2VudHJlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LnJlZ2lvbi5jbHVzdGVyPW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZShjb3VudC5jbHVzdGVyPXN1bShjb3VudC5yZWdpb24uY2x1c3RlciksIGZyYWN0PWNvdW50LnJlZ2lvbi5jbHVzdGVyL2NvdW50LmNsdXN0ZXIpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhjb3VudC5jbHVzdGVyKSkgJT4lCiAgZHBseXI6Om11dGF0ZShDbHVzdGVyLm89YXNfZmFjdG9yKENsdXN0ZXIpKQoKCnAuUEhFLmlkZW50aWNhbC5SZWdpb24uYnlaZXJvZGlzdC5jbHVzdGVyIDwtIGQuUEhFLmlkZW50aWNhbC5yZWdpb24uYnlaZXJvZGlzdC5jbHVzdGVyICU+JQogIGdncGxvdChhZXMoQ2x1c3Rlci5vLCBjb3VudC5yZWdpb24uY2x1c3RlciwgZmlsbD1waGVfY2VudHJlKSkgKyAKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHdpZHRoPTAuNjUsIHBvc2l0aW9uPSdmaWxsJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlVLSFNBXG5SZWdpb24iLCB2YWx1ZXM9UEhFLnJlZ2lvbi5jb2xzLmJyZXckcmVnaW9uLmNvbCwgYnJlYWtzPVBIRS5yZWdpb24uY29scy5icmV3JFVLSFNBLnJlZ2lvbiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHgudGhlbWUuYXhpcy5yb3RhdGUgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDQ1LDUpKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbD0yKSkgKwogIGxhYnMoeD0iSWRlbnRpY2FsIEdlbm9tZSBDbHVzdGVyIiwgeT0iUmVnaW9uIFByb3BvcnRpb24iLCBmaWxsPSJVS0hTQSBSZWdpb24iKSAKCgpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04fQpwLlBIRS5pZGVudGljYWwuYnlaZXJvZGlzdC5jbHVzdGVyLmJhcmNvbWJpIDwtIHBsb3RfZ3JpZChwLlBIRS5pZGVudGljYWwuR2VuZGVyT3JpZW50YXRpb24uYnlaZXJvZGlzdC5jbHVzdGVyICsgeC50aGVtZS5zdHJpcCwgcC5QSEUuaWRlbnRpY2FsLlJlZ2lvbi5ieVplcm9kaXN0LmNsdXN0ZXIsIG5jb2w9MSwgYXhpcz0icmx0IiwgYWxpZ249VCwgcmVsX2hlaWdodHMgPSBjKDIsMSksIGxhYmVscz1jKCJCIiwiQyIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplKQoKI3AuUEhFLmlkZW50aWNhbC5ieVplcm9kaXN0LmNsdXN0ZXIuYmFyY29tYmkKI3AuUEhFLmlkZW50aWNhbHMubmV0d29yay4wU05QCgpwbG90X2dyaWQocC5QSEUuaWRlbnRpY2Fscy5uZXR3b3JrLjBTTlAsIHAuUEhFLmlkZW50aWNhbC5ieVplcm9kaXN0LmNsdXN0ZXIuYmFyY29tYmksIG5jb2w9MSwgcmVsX2hlaWdodHM9YygyLDMpLCBsYWJlbHM9YygiQSIsIiIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplKQpgYGAKCgoKYGBge3J9ClBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cucmVnaW9uLnNjYXR0ZXJwaWUuZ3JvdXBzIDwtIFBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cgJT4lCiAgZHBseXI6OnNlbGVjdChDbHVzdGVyLCBUYXhhMSwgcGhlX2NlbnRyZSkgJT4lCiAgZHBseXI6OmRpc3RpbmN0KCkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KENsdXN0ZXIsIHBoZV9jZW50cmUpICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50LmNlbnRyZT1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUoeD1DbHVzdGVyLCB5PTMuNSkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbT0icGhlX2NlbnRyZSIsIHZhbHVlc19mcm9tPSJDb3VudC5jZW50cmUiLCB2YWx1ZXNfZmlsbD0wKSAlPiUKICBkcGx5cjo6c2VsZWN0KENsdXN0ZXIseCx5LHVuaXF1ZShQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnJHBoZV9jZW50cmUpKSAlPiUKICBkcGx5cjo6dW5ncm91cCgpICU+JQogIGRwbHlyOjptdXRhdGUoQ2x1c3Rlci5udW1lcmljPWFzLm51bWVyaWMoMToyNykpCiAgCgpwLlBIRS5pZGVudGljYWwuR2VuZGVyT3JpZW50YXRpb24uYnlaZXJvZGlzdC5jbHVzdGVyICsgCiAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2ZpbGwoKSAjKwogIAoKYGBgCgoKClwKR2V0IGEgZmV3IG1vcmUgc3RhdHMgb24gdGhlIGxhcmdlc3QgY2x1c3RlciAoQ2x1c3RlciA4KQpgYGB7cn0KI2QuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgJT4lIGZpbHRlcihDbHVzdGVyPT0iQ2x1c3RlcjgiKQoKUEhFLmlkZW50aWNhbHMubmV0d29yay5nZy5pZGVudGljYWwuY2x1c3RlcjggPC0gUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyAlPiUgZmlsdGVyKENsdXN0ZXI9PSJDbHVzdGVyOCIpICAlPiUKICBzZWxlY3QodmVydGV4Lm5hbWVzLCBPcmllbnRhdGlvbi5DbGFzcywgcGhlX2NlbnRyZSwgeWVhciwgVFBBX0xpbmVhZ2UsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBoaXZwb3MsIENsdXN0ZXIpCgpzb3J0KHVuaXF1ZShQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLmlkZW50aWNhbC5jbHVzdGVyOCR5ZWFyKSkKCmBgYAoKXApHZXQgc29tZSBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBoZXRlcm9zZXh1YWwgb25seSBjbHVzdGVycwpgYGB7cn0KUEhFLmlkZW50aWNhbHMubmV0d29yay5nZy5pZGVudGljYWxfaGV0ZXJvY2x1c3RlcnMgPC0gUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyAlPiUgZmlsdGVyKENsdXN0ZXIgJWluJSBjKCJDbHVzdGVyMTIiLCAiQ2x1c3RlcjIwIiwgIkNsdXN0ZXIyNyIpKSAgJT4lCiAgc2VsZWN0KHZlcnRleC5uYW1lcywgQ2x1c3RlciwgZ2VuZGVyX29yaWVudGF0aW9uLCBwaGVfY2VudHJlLCB5ZWFyLCBUUEFfTGluZWFnZSwgVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGhpdnBvcykgJT4lIAogIGRpc3RpbmN0KCkgJT4lCiAgYXJyYW5nZShDbHVzdGVyLCB5ZWFyLCBnZW5kZXJfb3JpZW50YXRpb24pCgpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLmlkZW50aWNhbF9oZXRlcm9jbHVzdGVycwpgYGAKXCAKQW5kIGRvIHRoZSBzYW1lIGZvciB0aGUgc21hbGwgbWl4ZWQvR0JNU00gY2x1c3RlcnMKYGBge3J9ClBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cuaWRlbnRpY2FsX25vdC5oZXRlcm9jbHVzdGVycyA8LSBQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnICU+JSBmaWx0ZXIoQ2x1c3RlciAlbm90aW4lIGMoIkNsdXN0ZXIxMiIsICJDbHVzdGVyMjAiLCAiQ2x1c3RlcjI3IiwgIkNsdXN0ZXI4IikpICAlPiUKICBzZWxlY3QodmVydGV4Lm5hbWVzLCBDbHVzdGVyLCBnZW5kZXJfb3JpZW50YXRpb24sIHBoZV9jZW50cmUsIHllYXIsIFRQQV9MaW5lYWdlLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSwgaGl2cG9zKSAlPiUgCiAgZGlzdGluY3QoKSAlPiUKICBhcnJhbmdlKENsdXN0ZXIsIHllYXIsIGdlbmRlcl9vcmllbnRhdGlvbikKUEhFLmlkZW50aWNhbHMubmV0d29yay5nZy5pZGVudGljYWxfbm90LmhldGVyb2NsdXN0ZXJzCmBgYAoKCgpXaGF0IHByb3BvcnRpb24gb2YgaGV0ZXJvc2V4dWFscyBoYXZlIGFuIGlkZW50aWNhbCBHQk1TTSBwYWlyZWQgZ2Vub21lPwpcCmBgYHtyfQoKIyBEZWxpbmVhdGUgaGV0ZXJvc2V4dWFsIGNsdXN0ZXJzCmQuUEhFLmlkZW50aWNhbC5oZXRlcm9zZXh1YWwuY2x1c3RlcnMgPC0gZC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciAlPiUgCiAgZHBseXI6Om11dGF0ZShpcy5oZXRlcm9zZXh1YWw9aWZlbHNlKGdlbmRlcl9vcmllbnRhdGlvbiVpbiUgYygiTVNXIiwgIldTTSIpLCAiaGV0ZXJvc2V4dWFsIiwgaWZlbHNlKGdlbmRlcl9vcmllbnRhdGlvbj09IkdCTVNNIiwiR0JNU00iLCAiVW5rbm93biIpKSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KENsdXN0ZXIsaXMuaGV0ZXJvc2V4dWFsKSAlPiUgCiAgZHBseXI6Om11dGF0ZShjb3VudC5oZXRlcm89c3VtKGNvdW50Lm9yaWVudC5jbHVzdGVyKSwgZnJhY3QuaGV0ZXJvPXN1bShjb3VudC5vcmllbnQuY2x1c3RlcikvY291bnQuY2x1c3RlcikgJT4lCiAgZHBseXI6OnVuZ3JvdXAoKSAlPiUKICBkcGx5cjo6ZmlsdGVyKGlzLmhldGVyb3NleHVhbD09ImhldGVyb3NleHVhbCIpICU+JSAKICBkcGx5cjo6c2VsZWN0KC1jKGNvdW50Lm9yaWVudC5jbHVzdGVyLCBnZW5kZXJfb3JpZW50YXRpb24sIGZyYWN0KSkgJT4lCiAgZHBseXI6OmRpc3RpbmN0KCkgJT4lCiAgZHBseXI6Om11dGF0ZShjbHVzdGVyLnR5cGU9aWZlbHNlKGZyYWN0LmhldGVybz09MSwgImhldGVyby5vbmx5IiwgIm90aGVyIikpCgpkLlBIRS5pZGVudGljYWwuaGV0ZXJvc2V4dWFsLmNsdXN0ZXJzIAoKIyBXaGF0IHByb3BvcnRpb24gb2YgaGV0ZXJvc2V4dWFscyAobj0yMCkgYXJlIGluIGEgaGV0ZXJvc2V4dWFsLW9ubHkgY2x1c3Rlcj8KZC5QSEUuaWRlbnRpY2FsLmhldGVyb3NleHVhbC5jbHVzdGVycyAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGNsdXN0ZXIudHlwZSkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudC5pbi5oZXRlcm8uY2x1c3Rlcj1zdW0oY291bnQuaGV0ZXJvKSkgJT4lIAogIGRwbHlyOjptdXRhdGUoZnJhY3QuaW4uaGV0ZXJvPWNvdW50LmluLmhldGVyby5jbHVzdGVyL3N1bShjb3VudC5pbi5oZXRlcm8uY2x1c3RlcikpCiAgCgojbGVmdF9qb2luKFBIRS5pZGVudGljYWxzLm5ldHdvcmsuY29tcG9uZW50c1ssYygiVGF4YTEiLCJDbHVzdGVyIildLCBQSEUubWV0YWRhdGEubGlua2VkWyxjKCJTYW1wbGVfTmFtZSIsInBoZV9jZW50cmUiLCJsb25kb24iLCJ5ZWFyIiwiYWdlX2dyb3VwIiwidWtib3JuIiwiZ2VuZGVyX29yaWVudGF0aW9uIiwiaGl2cG9zIiwiVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UiLCJUUEFfTGluZWFnZSIpXSwgYnk9YygiVGF4YTEiPSJTYW1wbGVfTmFtZSIpKQpgYGAKCgoKCgo=